<!DOCTYPE html>
            
<HTML>
<HEAD>
<meta name="booktitle" content="Developing Applications With Objective Caml" >
 <meta charset="ISO-8859-1"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<META name="GENERATOR" content="hevea 1.05-7 of 2000-02-24">
<META NAME="Author" CONTENT="Christian.Queinnec@lip6.fr">
<LINK rel=stylesheet type="text/css" href="videoc-ocda.css">
<script language="JavaScript" src="videoc.js"><!--
//--></script>
<TITLE>
 Constructing a Graphical Interface
</TITLE>
</HEAD>
<BODY class="regularBody">
<A HREF="book-ora123.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora125.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
<HR>

<H2> Constructing a Graphical Interface</H2>
<A NAME="@concepts279"></A>
The implementation of a graphical interface for a program is a tedious job if
the tools at your disposal are not powerful enough, as this is the case 
with the 
<TT>Graphics</TT> library. The user-friendliness of a program derives in part from
its interface. To ease the task of creating a graphical interface we will start by creating a new library 
called <TT>Awi</TT>
which sits on top of <TT>Graphics</TT> and then we will use 
it as a simple module to
help us construct the interface for an application.<BR>
<BR>
This graphical interface manipulates components. A component 
is a region of
the main window which can be displayed in a certain graphical context and can handle 
events that are sent to it. There are basically two kinds of components: simple components,
such as a confirmation button or a text entry field, and containers which allow
other components to be placed within them. A component can only be attached to a single 
container. Thus the interface of an application is built as a tree whose root corresponds to the
main container (the graphics window), the nodes are also containers 
and the leaves are simple
components or empty containers. This treelike 
structure helps us to propagate events arising from
 user interaction. If a container receives an event it checks whether one of its children
can handle it, if so then it sends the event to that child, otherwise it 
deals with the event using its own handler.<BR>
<BR>
The component is the essential element in this library. We define it as a record which
contains details of size, a graphic context, the parent and child components along with
functions for display and for handling events.
Containers include a function for displaying their components. To define
the 
<I>component</I> type, we build the types for the graphics
context, for events and for initialization options.
A graphical context is used to contain the details of
``graphical styles'' such as the colors of the background and foreground,
the size of the characters, the current location of the component
and the fonts that have been chosen. Then must we define the kinds of events
which can be sent to the component. These are more varied than
those in the <TT>Graphics</TT> library on which they are based.
We include a simple option mechanism which helps us to configure
graphics contexts or components. One implementation difficulty arises in 
positioning components within a container.<BR>
<BR>
The general event handling loop receives physical events from the input 
function of the <TT>Graphics</TT> library, decides whether other events 
should be generated as a result of these physical events, and then sends
them to the root container. We shall consider the following components:
text display, buttons, list boxes, input regions and enriched components.
Next we will show how the components are assembled to construct graphical interfaces,
illustrating this with a program to convert between Francs and Euros. The
various components of this application communicate with each other over 
a shared piece of state.<BR>
<BR>
<A NAME="toc166"></A>
<H3> Graphics Context, Events and Options</H3>
Let's start by defining the base types along with the functions to initialize and modify
graphics contexts, events and options. There is also an option type to help us 
parametrize the functions which create graphical objects.<BR>
<BR>

<H4> Graphics Context</H4>
The graphics context allows us to keep track of the foreground and background colors,
the font, its size, the current cursor position, and line width.
This results in the following type.<BR>
<BR>


<PRE><BR><CODE> </CODE><B>type</B><CODE> </CODE><CODE> </CODE>g_context<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>bcol<CODE> </CODE><CODE>:</CODE><CODE> </CODE>Graphics.color;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>fcol<CODE> </CODE><CODE>:</CODE><CODE> </CODE>Graphics.color;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>font<CODE> </CODE><CODE>:</CODE><CODE> </CODE><CODE> </CODE>string;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>font_size<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>lw<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>x<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>y<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int<CODE> </CODE>};;<BR>

</PRE>
<BR>
<BR>
The <TT>make_default_context</TT> function creates a new graphics
context containing default values
<A NAME="text34" HREF="book-ora126.html#note34"><SUP><FONT SIZE=2>1</FONT></SUP></A>.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>default_font<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>"fixed"</CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>default_font_size<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>1</CODE><CODE>2</CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>make_default_context<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE>bcol<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.white;<CODE> </CODE>fcol<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.black;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>font<CODE> </CODE><CODE>=</CODE><CODE> </CODE>default_font;<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>font_size<CODE> </CODE><CODE>=</CODE><CODE> </CODE>default_font_size;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>lw<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>1</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>x<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>0</CODE>;<CODE> </CODE>y<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>0</CODE>;};;<BR><CODE>val default_font : string = "fixed"</CODE><BR><CODE>val default_font_size : int = 12</CODE><BR><CODE>val make_default_context : unit -&gt; g_context = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
Access functions for the individual fields allow us to retrieve their values
without knowing the implementation of the type itself.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>get_gc_bcol<CODE> </CODE>gc<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>bcol<CODE> </CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>get_gc_fcol<CODE> </CODE>gc<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>fcol<CODE> </CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>get_gc_font<CODE> </CODE><CODE> </CODE>gc<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>font<BR><CODE> </CODE><B>let</B><CODE> </CODE>get_gc_font_size<CODE> </CODE><CODE> </CODE>gc<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>font_size<BR><CODE> </CODE><B>let</B><CODE> </CODE>get_gc_lw<CODE> </CODE>gc<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>lw<BR><CODE> </CODE><B>let</B><CODE> </CODE>get_gc_cur<CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>gc<CODE>.</CODE>x<CODE>,</CODE>gc<CODE>.</CODE>y<TT>)</TT>;;<BR><CODE>val get_gc_bcol : g_context -&gt; Graphics.color = &lt;fun&gt;</CODE><BR><CODE>val get_gc_fcol : g_context -&gt; Graphics.color = &lt;fun&gt;</CODE><BR><CODE>val get_gc_font : g_context -&gt; string = &lt;fun&gt;</CODE><BR><CODE>val get_gc_font_size : g_context -&gt; int = &lt;fun&gt;</CODE><BR><CODE>val get_gc_lw : g_context -&gt; int = &lt;fun&gt;</CODE><BR><CODE>val get_gc_cur : g_context -&gt; int * int = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The functions to modify those fields work on the same principle.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>set_gc_bcol<CODE> </CODE>gc<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>bcol<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<BR><CODE> </CODE><B>let</B><CODE> </CODE>set_gc_fcol<CODE> </CODE>gc<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>fcol<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<BR><CODE> </CODE><B>let</B><CODE> </CODE>set_gc_font<CODE> </CODE><CODE> </CODE>gc<CODE> </CODE>f<CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>font<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>f<BR><CODE> </CODE><B>let</B><CODE> </CODE>set_gc_font_size<CODE> </CODE><CODE> </CODE>gc<CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>font_size<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>s<BR><CODE> </CODE><B>let</B><CODE> </CODE>set_gc_lw<CODE> </CODE>gc<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>lw<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>i<BR><CODE> </CODE><B>let</B><CODE> </CODE>set_gc_cur<CODE> </CODE>gc<CODE> </CODE><TT>(</TT>a<CODE>,</CODE>b<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>gc<CODE>.</CODE>x<CODE>&lt;-</CODE><CODE> </CODE>a;<CODE> </CODE>gc<CODE>.</CODE>y<CODE>&lt;-</CODE>b;;<BR><CODE>val set_gc_bcol : g_context -&gt; Graphics.color -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val set_gc_fcol : g_context -&gt; Graphics.color -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val set_gc_font : g_context -&gt; string -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val set_gc_font_size : g_context -&gt; int -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val set_gc_lw : g_context -&gt; int -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val set_gc_cur : g_context -&gt; int * int -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We can thus create new contexts, and read and write
various fields of a value of the <I>g_context</I> type.<BR>
<BR>
The <TT>use_gc</TT> function applies the data of a graphic context to 
the graphical window.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>use_gc<CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.set_color<CODE> </CODE><TT>(</TT>get_gc_fcol<CODE> </CODE>gc<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.set_font<CODE> </CODE><TT>(</TT>get_gc_font<CODE> </CODE>gc<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.set_text_size<CODE> </CODE><TT>(</TT>get_gc_font_size<CODE> </CODE>gc<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.set_line_width<CODE> </CODE><TT>(</TT>get_gc_lw<CODE> </CODE>gc<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>a<CODE>,</CODE>b<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>get_gc_cur<CODE> </CODE>gc<CODE> </CODE><B>in</B><CODE> </CODE>Graphics.moveto<CODE> </CODE>a<CODE> </CODE>b;;<BR><CODE>val use_gc : g_context -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
Some data, such as the background color, are not directly used by the 
<TT>Graphics</TT> library and do not appear in the 
<TT>use_gc</TT> function.<BR>
<BR>

<H4> Events</H4>
The <TT>Graphics</TT> library only contains a limited number
of interaction events: mouse click, mouse movement and key press.
We want to enrich the kind of event that arises from interaction by
integrating events arising at the component level. To this end
we define the type <I>rich_event</I>:<BR>
<BR>


<PRE><BR># <B>type</B><CODE> </CODE>rich_event<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>MouseDown<CODE> </CODE><CODE>|</CODE><CODE> </CODE>MouseUp<CODE> </CODE><CODE>|</CODE><CODE> </CODE>MouseDrag<CODE> </CODE><CODE>|</CODE><CODE> </CODE>MouseMove<BR><CODE> </CODE><CODE>|</CODE><CODE> </CODE>MouseEnter<CODE> </CODE><CODE>|</CODE><CODE> </CODE>MouseExit<CODE> </CODE><CODE>|</CODE><CODE> </CODE>Exposure<CODE> </CODE><BR><CODE> </CODE><CODE>|</CODE><CODE> </CODE>GotFocus<CODE> </CODE><CODE>|</CODE><CODE> </CODE>LostFocus<CODE> </CODE><CODE>|</CODE><CODE> </CODE>KeyPress<CODE> </CODE><CODE>|</CODE><CODE> </CODE>KeyRelease;;<BR>

</PRE>
<BR>
<BR>
To create such events it is necessary to keep a history of previous events.
The <TT>MouseDown</TT> and <TT>MouseMove</TT> events correspond to
mouse events (clicking and moving) which are created by <TT>Graphics</TT>.
Other mouse events are created by virtue of either the previous event
<TT>MouseUp</TT>, or the last component which handled a physical event
<TT>MouseExit</TT>. The <TT>Exposure</TT> event corresponds to a request
to redisplay a component. The concept of focus expresses that a 
given component is interested in a certain kind of event. Typically
the input of text to a component which has grabbed the focus
means that this component alone will handle <TT>KeyPress</TT> and
<TT>KeyRelease</TT> events. A <TT>MouseDown</TT> event on a text input
component hands over the input focus to it and takes it away from the
component which had it before.<BR>
<BR>
These new events are created by the event handling loop described on 
page <A HREF="book-ora124.html#gest-events">??</A>.<BR>
<BR>

<H4> Options</H4>
A graphical interface needs rules for describing the creation options
for graphical objects (components, graphics contexts). If we wish to
create a graphics context with a certain color it is currently necessary
to construct it with the default values and then to call
the two functions to modify the color fields in that context. In the
case of more complex graphic objects this soon becomes tedious.
Since we want to extend these options as we build up the components
of the library, we need an ``extensible'' sum type. The only one 
provided by Objective CAML is the <I>exn</I> type used for exceptions.
Because using<I>exn</I> for handling options would affect the clarity 
of our programs we will only use this type for real exceptions. 
Instead, we will simulate an extensible 
sum type using pseudo constructors represented by character strings.
We define the type <I>opt_val</I> for the values of these options. An
option is a tuple whose first element is the name of the option and the
second its value. The <I>lopt</I> type encompasses a list of such options.<BR>
<BR>


<PRE><BR># <B>type</B><CODE> </CODE>opt_val<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Copt<CODE> </CODE><B>of</B><CODE> </CODE>Graphics.color<CODE> </CODE><CODE>|</CODE><CODE> </CODE>Sopt<CODE> </CODE><B>of</B><CODE> </CODE>string<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>Iopt<CODE> </CODE><B>of</B><CODE> </CODE>int<CODE> </CODE><CODE>|</CODE><CODE> </CODE>Bopt<CODE> </CODE><B>of</B><CODE> </CODE>bool;;<CODE> </CODE><BR># <B>type</B><CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>string<CODE> </CODE><CODE>*</CODE><CODE> </CODE>opt_val<TT>)</TT><CODE> </CODE>list<CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
The decoding functions take as input a list of options, an option name and a
default value. If the name belongs to the list then the associated value is
returned, if not then we get the default value. We show here only the decoding
functions for integers and booleans, the others work on the same principle.<BR>
<BR>


<PRE><BR># <B>exception</B><CODE> </CODE>OptErr;;<BR><CODE>exception OptErr</CODE><BR># <B>let</B><CODE> </CODE>theInt<CODE> </CODE>lo<CODE> </CODE>name<CODE> </CODE>default<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>List.assoc<CODE> </CODE>name<CODE> </CODE>lo<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Iopt<CODE> </CODE>i<CODE> </CODE>-&gt;<CODE> </CODE>i<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE>raise<CODE> </CODE>OptErr<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Not_found<CODE> </CODE>-&gt;<CODE> </CODE>default;;<BR><CODE>val theInt : ('a * opt_val) list -&gt; 'a -&gt; int -&gt; int = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>theBool<CODE> </CODE>lo<CODE> </CODE>name<CODE> </CODE>default<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>List.assoc<CODE> </CODE>name<CODE> </CODE>lo<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Bopt<CODE> </CODE>b<CODE> </CODE>-&gt;<CODE> </CODE>b<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE>raise<CODE> </CODE>OptErr<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Not_found<CODE> </CODE>-&gt;<CODE> </CODE>default;;<BR><CODE>val theBool : ('a * opt_val) list -&gt; 'a -&gt; bool -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We can now write a function to create a graphics context using a list of 
options in the following manner:<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>set_gc<CODE> </CODE>gc<CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc_bcol<CODE> </CODE>gc<CODE> </CODE><TT>(</TT>theColor<CODE> </CODE>lopt<CODE> </CODE><CODE>"Background"</CODE><CODE> </CODE><TT>(</TT>get_gc_bcol<CODE> </CODE>gc<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc_fcol<CODE> </CODE>gc<CODE> </CODE><TT>(</TT>theColor<CODE> </CODE>lopt<CODE> </CODE><CODE>"Foreground"</CODE><CODE> </CODE><TT>(</TT>get_gc_fcol<CODE> </CODE>gc<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc_font<CODE> </CODE>gc<CODE> </CODE><TT>(</TT>theString<CODE> </CODE>lopt<CODE> </CODE><CODE>"Font"</CODE><CODE> </CODE><TT>(</TT>get_gc_font<CODE> </CODE>gc<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc_font_size<CODE> </CODE>gc<CODE> </CODE><TT>(</TT>theInt<CODE> </CODE>lopt<CODE> </CODE><CODE>"FontSize"</CODE><CODE> </CODE><TT>(</TT>get_gc_font_size<CODE> </CODE>gc<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc_lw<CODE> </CODE>gc<CODE> </CODE><TT>(</TT>theInt<CODE> </CODE>lopt<CODE> </CODE><CODE>"LineWidth"</CODE><CODE> </CODE><TT>(</TT>get_gc_lw<CODE> </CODE>gc<TT>)</TT><TT>)</TT>;;<BR><CODE>val set_gc : g_context -&gt; (string * opt_val) list -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
This allows us to ignore the order in which the options are passed in.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>make_default_context<CODE> </CODE>()<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc<CODE> </CODE>dc<CODE> </CODE><CODE>[</CODE><CODE> </CODE><CODE>"Foreground"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>Graphics.blue;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>Graphics.yellow<CODE>]</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>dc;;<BR><CODE>- : g_context =</CODE><BR><CODE>{bcol=16776960; fcol=255; font="fixed"; font_size=12; lw=1; x=0; y=0}</CODE><BR>

</PRE>
<BR>
<BR>
This results in a fairly flexible system which unfortunately partially
evades the type system. The name of an option is of the type <I>string</I> 
and nothing prevents the construction of a nonexistant name. The result
is simply that the value is ignored.
<BR>
<BR>
<A NAME="toc167"></A>
<H3> Components and Containers</H3>The component is the essential building block of this library. We want
to be able to create components and then easily assemble
them to construct interfaces. They must be able to display themselves,
to recognize an event destined for them, and to handle that event.
Containers must be able to receive events from other
components or to hand them on. We assume that a component can only
be added to one container.<BR>
<BR>

<H4> Construction of Components</H4>
A value of type <I>component</I> has a size (<TT>w</TT>
and <TT>h</TT>), an absolute position in the main window
(<TT>x</TT> and <TT>y</TT>), a graphics context used when it is displayed
(<TT>gc</TT>), a flag to show whether it is a container
(<TT>container</TT>), a parent - if it is itself attached to a container
(<TT>parent</TT>), a list of child components
(<TT>children</TT>) and four functions to handle positioning of components.
These control how children are positioned within a component
(<TT>layout</TT>), how the component is displayed (<TT>display</TT>),
whether any given point is considered to be within the area of 
the component (<TT>mem</TT>) and finally a function for event handling
(<TT>listener</TT>) which returns 
<TT>true</TT> if the event was handled and <TT>false</TT> otherwise. The parameter of the
<TT>listener</TT> is of type (type
<I>rich_status</I>) and contains the name of the event
the lowlevel event information coming from the <TT>Graphics</TT> module, information
on the keyboard focus and the general focus,
as well as the last component to have handled an event. So we
arrive at the following mutually recursive declarations:


<PRE><BR># <B>type</B><CODE> </CODE><CODE> </CODE>component<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE><B>mutable</B><CODE> </CODE>info<CODE> </CODE><CODE>:</CODE><CODE> </CODE>string;<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>x<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>y<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>w<CODE> </CODE><CODE>:</CODE>int<CODE> </CODE>;<CODE> </CODE><B>mutable</B><CODE> </CODE>h<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>gc<CODE> </CODE><CODE>:</CODE><CODE> </CODE>g_context;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>container<CODE> </CODE><CODE>:</CODE><CODE> </CODE>bool;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>parent<CODE> </CODE><CODE>:</CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>component<CODE> </CODE>list;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>children<CODE> </CODE><CODE>:</CODE><CODE> </CODE><CODE> </CODE>component<CODE> </CODE>list;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>layout_options<CODE> </CODE><CODE>:</CODE><CODE> </CODE>lopt;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>layout<CODE> </CODE><CODE>:</CODE><CODE> </CODE>component<CODE> </CODE>-&gt;<CODE> </CODE><CODE> </CODE>lopt<CODE> </CODE>-&gt;<CODE> </CODE>unit;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>display<CODE> </CODE><CODE>:</CODE><CODE> </CODE>unit<CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE>unit;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>mem<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int<CODE> </CODE><CODE>*</CODE><CODE> </CODE>int<CODE> </CODE>-&gt;<CODE> </CODE>bool;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>listener<CODE> </CODE><CODE>:</CODE><CODE> </CODE>rich_status<CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE>bool<CODE> </CODE>}<BR><CODE> </CODE><B>and</B><CODE> </CODE>rich_status<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE>re<CODE> </CODE><CODE>:</CODE><CODE> </CODE>rich_event;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>stat<CODE> </CODE><CODE>:</CODE><CODE> </CODE>Graphics.status;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>key_focus<CODE> </CODE><CODE>:</CODE><CODE> </CODE>component;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>gen_focus<CODE> </CODE><CODE>:</CODE><CODE> </CODE>component;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>last<CODE> </CODE><CODE>:</CODE><CODE> </CODE>component};;<BR>

</PRE>
<BR>
<BR>
We access the data fields of a component with the following functions.


<PRE><BR># <B>let</B><CODE> </CODE>get_gc<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c<CODE>.</CODE>gc;;<BR><CODE>val get_gc : component -&gt; g_context = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>is_container<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c<CODE>.</CODE>container;;<BR><CODE>val is_container : component -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The following three functions define the default behavior of a component.
The function to test whether a given mouse position applies to a
 given component
(<TT>in_rect</TT>) checks that the coordinate is within the rectangle
defined by the coordinates of the component. The default display function
(<TT>display_rect</TT>) fills the rectangle of the component with the 
background color found in the graphic context of that component. 
The default layout function
(<TT>direct_layout</TT>) places components relatively within their containers.
Valid options are <TT>"PosX"</TT> and
<TT>"PosY"</TT>, corresponding to the coordinates relative to the container.


<PRE><BR># <B>let</B><CODE> </CODE>in_rect<CODE> </CODE><CODE> </CODE>c<CODE> </CODE><CODE> </CODE><TT>(</TT>xp<CODE>,</CODE>yp<TT>)</TT><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>xp<CODE> </CODE><CODE>&gt;=</CODE><CODE> </CODE>c<CODE>.</CODE>x<TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>xp<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE>c<CODE>.</CODE>x<CODE> </CODE><CODE>+</CODE><CODE> </CODE>c<CODE>.</CODE>w<TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>yp<CODE> </CODE><CODE>&gt;=</CODE><CODE> </CODE>c<CODE>.</CODE>y<TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>yp<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE><CODE>+</CODE><CODE> </CODE>c<CODE>.</CODE>h<TT>)</TT><CODE> </CODE>;;<BR><CODE>val in_rect : component -&gt; int * int -&gt; bool = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>display_rect<CODE> </CODE>c<CODE> </CODE>()<CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>get_gc<CODE> </CODE>c<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.set_color<CODE> </CODE><TT>(</TT>get_gc_bcol<CODE> </CODE>gc<TT>)</TT>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.fill_rect<CODE> </CODE>c<CODE>.</CODE>x<CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE>c<CODE>.</CODE>w<CODE> </CODE>c<CODE>.</CODE>h<CODE> </CODE>;;<BR><CODE>val display_rect : component -&gt; unit -&gt; unit = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>direct_layout<CODE> </CODE>c<CODE> </CODE>c1<CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>px<CODE> </CODE><CODE>=</CODE><CODE> </CODE>theInt<CODE> </CODE>lopt<CODE> </CODE><CODE>"PosX"</CODE><CODE> </CODE><CODE>0</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>py<CODE> </CODE><CODE>=</CODE><CODE> </CODE>theInt<CODE> </CODE>lopt<CODE> </CODE><CODE>"PosY"</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>in</B><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>x<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE>.</CODE>x<CODE> </CODE><CODE>+</CODE><CODE> </CODE>px;<CODE> </CODE>c1<CODE>.</CODE>y<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE><CODE>+</CODE><CODE> </CODE>py<CODE> </CODE>;;<BR><CODE>val direct_layout :</CODE><BR><CODE>  component -&gt; component -&gt; (string * opt_val) list -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
It is now possible to define a component using the function
<TT>create_component</TT> which takes width and height as parameters
and uses the three preceding functions.


<PRE><BR># <B>let</B><CODE> </CODE>create_component<CODE> </CODE>iw<CODE> </CODE>ih<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>{info<CODE>=</CODE><CODE>"Anonymous"</CODE>;<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>x<CODE>=</CODE><CODE>0</CODE>;<CODE> </CODE>y<CODE>=</CODE><CODE>0</CODE>;<CODE> </CODE>w<CODE>=</CODE>iw;<CODE> </CODE>h<CODE>=</CODE>ih;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>make_default_context()<CODE> </CODE>;<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>container<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>false</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>parent<CODE> </CODE><CODE>=</CODE><CODE> </CODE>[];<CODE> </CODE>children<CODE> </CODE><CODE>=</CODE><CODE> </CODE>[];<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>layout_options<CODE> </CODE><CODE>=</CODE><CODE> </CODE>[];<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>layout<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>a<CODE> </CODE>b<CODE> </CODE>-&gt;<CODE> </CODE>()<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>display<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>()<CODE> </CODE>-&gt;<CODE> </CODE>()<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>mem<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>s<CODE> </CODE>-&gt;<CODE> </CODE><B>false</B><TT>)</TT>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>listener<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>s<CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE><B>false</B><TT>)</TT>;}<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>dc<CODE>.</CODE>layout<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>direct_layout<CODE> </CODE>dc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>dc<CODE>.</CODE>mem<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>in_rect<CODE> </CODE>dc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>dc<CODE>.</CODE>display<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>display_rect<CODE> </CODE>dc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>dc<CODE> </CODE>;;<BR><CODE>val create_component : int -&gt; int -&gt; component = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We then define the following empty component:


<PRE><BR># <B>let</B><CODE> </CODE>empty_component<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_component<CODE> </CODE><CODE>0</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE>;;<BR>

</PRE>

This is used as a default value when we construct values which need to contain
at least one component (for example a value of type 
<I>rich_status</I>).<BR>
<BR>

<H4> Adding Child Components</H4>
The difficult part of adding a component to a container is how to position
the component within the container.
The <TT>layout</TT> field contains this positioning function.
It takes a component (a child) and a list of options and calculates the
new coordinates of the child within the container. Different options can be
used according to the positioning function. We describe several layout functions
when we talk about about the 
<I>panel</I> component (see <I>below</I>, page <A HREF="book-ora124.html#comp-panel">??</A>). 
Here we simply describe the mechanism for propagating the display function through
the tree of components, coordinate changes, and propagating events. The propagation
of actions makes intensive use of the 
<TT>List.iter</TT> function, which applies a function to all the elements of a list.<BR>
<BR>
The function <TT>change_coord</TT> applies a relative change to the coordinates of a 
component and those of all its children.


<PRE><BR># <B>let</B><CODE> </CODE><B>rec</B><CODE> </CODE>change_coord<CODE> </CODE><CODE> </CODE>c<CODE> </CODE><TT>(</TT>dx<CODE>,</CODE>dy<TT>)</TT><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>c<CODE>.</CODE>x<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE>.</CODE>x<CODE> </CODE><CODE>+</CODE><CODE> </CODE>dx;<CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE><CODE>+</CODE><CODE> </CODE>dy;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.iter<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>s<CODE> </CODE>-&gt;<CODE> </CODE>change_coord<CODE> </CODE>s<CODE> </CODE><TT>(</TT>dx<CODE>,</CODE>dy<TT>)</TT><CODE> </CODE><TT>)</TT><CODE> </CODE>c<CODE>.</CODE>children;;<BR><CODE>val change_coord : component -&gt; int * int -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The <TT>add_component</TT> function checks that the conditions for adding a component have been met
and then joins the parent (<TT>c</TT>) and the child
(<TT>c1</TT>). The list of positioning options is retained in the child 
component, which allows us to reuse them when the positioning function
of the parent changes. The list of options passed to this function are those used 
by the positioning function. There are three conditions which need to be prohibited: the child 
component is already a parent, the parent is not a container or the child is too large for parent<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>add_component<CODE> </CODE><CODE> </CODE>c<CODE> </CODE>c1<CODE> </CODE>lopt<CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>c1<CODE>.</CODE>parent<CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE>[]<CODE> </CODE><B>then</B><CODE> </CODE>failwith<CODE> </CODE><CODE>"add_component: already a parent"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>not<CODE> </CODE><TT>(</TT>is_container<CODE> </CODE>c<CODE> </CODE><TT>)</TT><CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>failwith<CODE> </CODE><CODE>"add_component: not a container"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>c1<CODE>.</CODE>x<CODE> </CODE><CODE>+</CODE><CODE> </CODE>c1<CODE>.</CODE>w<CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE>c<CODE>.</CODE>w<TT>)</TT><CODE> </CODE><CODE>||</CODE><CODE> </CODE><TT>(</TT>c1<CODE>.</CODE>y<CODE> </CODE><CODE>+</CODE><CODE> </CODE>c1<CODE>.</CODE>h<CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE>c<CODE>.</CODE>h<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>then</B><CODE> </CODE>failwith<CODE> </CODE><CODE>"add_component: bad position"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c<CODE>.</CODE>layout<CODE> </CODE><CODE> </CODE>c1<CODE> </CODE>lopt;<CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>layout_options<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>lopt;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.iter<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>s<CODE> </CODE>-&gt;<CODE> </CODE>change_coord<CODE> </CODE>s<CODE> </CODE><TT>(</TT>c1<CODE>.</CODE>x<CODE>,</CODE>c1<CODE>.</CODE>y<TT>)</TT><TT>)</TT><CODE> </CODE>c1<CODE>.</CODE>children;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c<CODE>.</CODE>children<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c1::c<CODE>.</CODE>children;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>parent<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE> </CODE><CODE>[</CODE>c<CODE>]</CODE><CODE> </CODE>;;<BR><CODE>val add_component : component -&gt; component -&gt; lopt -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The removal of a component from some level in the tree, implemented by
the following function, entails both a change to the link between the parent
and the child and also a change to the coordinates of the child and all its own children:


<PRE><BR># <B>let</B><CODE> </CODE>remove_component<CODE> </CODE>c<CODE> </CODE>c1<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c<CODE>.</CODE>children<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>List.filter<CODE> </CODE><TT>(</TT><TT>(</TT><CODE>!=</CODE><TT>)</TT><CODE> </CODE>c1<TT>)</TT><CODE> </CODE><CODE> </CODE>c<CODE>.</CODE>children;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>parent<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>List.filter<CODE> </CODE><TT>(</TT><TT>(</TT><CODE>!=</CODE><TT>)</TT><CODE> </CODE>c<TT>)</TT><CODE> </CODE>c1<CODE>.</CODE>parent;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.iter<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>s<CODE> </CODE>-&gt;<CODE> </CODE>change_coord<CODE> </CODE>s<CODE> </CODE><TT>(</TT><CODE>-</CODE><CODE> </CODE>c1<CODE>.</CODE>x<CODE>,</CODE><CODE> </CODE><CODE>-</CODE><CODE> </CODE>c1<CODE>.</CODE>y<TT>)</TT><TT>)</TT><CODE> </CODE>c1<CODE>.</CODE>children;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>x<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE>0</CODE>;<CODE> </CODE>c1<CODE>.</CODE>y<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE>0</CODE>;;<BR><CODE>val remove_component : component -&gt; component -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
A change to the positioning function of a container depends on whether it
has any children. If it does not the change is immediate. Otherwise we must first 
remove the children of the container, modify the container's positioning function
and then add the components back in with the same options used when they were
originally added.


<PRE><BR># <B>let</B><CODE> </CODE>set_layout<CODE> </CODE>f<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>c<CODE>.</CODE>children<CODE> </CODE><CODE>=</CODE><CODE> </CODE>[]<CODE> </CODE><B>then</B><CODE> </CODE>c<CODE>.</CODE>layout<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>f<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ls<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c<CODE>.</CODE>children<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.iter<CODE> </CODE><TT>(</TT>remove_component<CODE> </CODE>c<TT>)</TT><CODE> </CODE>ls;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c<CODE>.</CODE>layout<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>f;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.iter<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>s<CODE> </CODE>-&gt;<CODE> </CODE>add_component<CODE> </CODE>c<CODE> </CODE>s<CODE> </CODE>s<CODE>.</CODE>layout_options<TT>)</TT><CODE> </CODE>ls;;<BR><CODE>val set_layout : (component -&gt; lopt -&gt; unit) -&gt; component -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>

This is why we kept the list of positioning options. If the list of options is not 
recognized by the new function it uses the defaults.<BR>
<BR>
When a component is displayed, the display event must be propagated to
its children. The container is displayed behind its children. The order of display of 
the children is unimportant because they never overlap.


<PRE><BR># <B>let</B><CODE> </CODE><B>rec</B><CODE> </CODE>display<CODE> </CODE><CODE> </CODE>c<CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>c<CODE>.</CODE>display<CODE> </CODE>();<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.iter<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>cx<CODE> </CODE>-&gt;<CODE> </CODE>display<CODE> </CODE>cx<CODE> </CODE><TT>)</TT><CODE> </CODE>c<CODE>.</CODE>children;;<BR><CODE>val display : component -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
<A NAME="toc168"></A>
<H3> Event Handling</H3>
<A NAME="gest-events"></A>
The handling of physical events (mouse click, key press, mouse movement) uses the
<TT>Graphics.wait_next_event</TT> function (see page <A HREF="book-ora050.html#sec-fn-event">??</A>)
which returns a physical status
(of type <TT>Graphics.status</TT>) following any user interaction. This physical status
is used to calculate a rich status
(of type <I>rich_status</I>) containing the event type
(of type <I>rich_event</I>), the physical status, the components possessing the
keyboard focus and the general focus along with the last component which successfully 
handled such an event. The general focus is a component which accepts all events.<BR>
<BR>
Next we describe the functions for the manipulating of rich events, the propagation of this
status information to components for them to be handled, the creation of the information
and the main event-handling loop.<BR>
<BR>

<H4> Functions used on Status</H4>The following functions read the values of the mouse position and the focus. Functions
on focus need a further parameter: the component which is capturing or losing that focus.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>get_event<CODE> </CODE>e<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>re;;<BR># <B>let</B><CODE> </CODE>get_mouse_x<CODE> </CODE>e<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>stat<CODE>.</CODE>Graphics.mouse_x;;<BR># <B>let</B><CODE> </CODE>get_mouse_y<CODE> </CODE>e<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>stat<CODE>.</CODE>Graphics.mouse_y;;<BR># <B>let</B><CODE> </CODE>get_key<CODE> </CODE>e<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>stat<CODE>.</CODE>Graphics.key;;<BR><BR># <B>let</B><CODE> </CODE>has_key_focus<CODE> </CODE>e<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>key_focus<CODE> </CODE><CODE>==</CODE><CODE> </CODE>c;;<BR># <B>let</B><CODE> </CODE>take_key_focus<CODE> </CODE>e<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>key_focus<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c;;<BR># <B>let</B><CODE> </CODE>lose_key_focus<CODE> </CODE>e<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>key_focus<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>empty_component;;<CODE> </CODE><BR># <B>let</B><CODE> </CODE>has_gen_focus<CODE> </CODE>e<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>gen_focus<CODE> </CODE><CODE>==</CODE><CODE> </CODE>c;;<BR># <B>let</B><CODE> </CODE>take_gen_focus<CODE> </CODE>e<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>gen_focus<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c;;<BR># <B>let</B><CODE> </CODE>lose_gen_focus<CODE> </CODE>e<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>gen_focus<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>empty_component;;<CODE> </CODE><BR>

</PRE>
<BR>
<BR>

<H4> Propagation of Events</H4>
A rich event is sent to a component to be handled. Analogous to the
display mechanism discussed earlier, child components have priority over their
parents for handling simple mouse movement. If a component receives 
status information associated with an event, it looks to see if it has a child which
can handle it. If so, the child returns <TT>true</TT> otherwise <TT>false</TT>. 
If no child can handle the event, the parent component tries to use the
function in its own <TT>listener</TT> field.<BR>
<BR>
Status information coming from keyboard activity is propagated differently. 
The parent component looks to see if it possesses the keyboard focus, and if so it handles
the event, otherwise it propagates to its children.<BR>
<BR>
Some events are produced as a result of handling an initial event. For example,
if one component captures the focus, then this means another has lost it. Such events
are handled immediately by the target component. This is the same with the entry and exit
events caused when the mouse is moved between different components.<BR>
<BR>
The <TT>send_event</TT> function takes a value of type
<I>rich_status</I> and a component. It returns a boolean indicating whether 
the event was handled or not.


<PRE><BR># <B>let</B><CODE> </CODE><B>rec</B><CODE> </CODE>send_event<CODE> </CODE>rs<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>get_event<CODE> </CODE>rs<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>MouseDown<CODE> </CODE><CODE>|</CODE><CODE> </CODE>MouseUp<CODE> </CODE><CODE>|</CODE><CODE> </CODE>MouseDrag<CODE> </CODE><CODE>|</CODE><CODE> </CODE>MouseMove<CODE> </CODE>-&gt;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>c<CODE>.</CODE>mem<TT>(</TT>get_mouse_x<CODE> </CODE>rs<CODE>,</CODE><CODE> </CODE>get_mouse_y<CODE> </CODE>rs<TT>)</TT><CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE><CODE> </CODE>List.exists<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>sun<CODE> </CODE>-&gt;<CODE> </CODE>send_event<CODE> </CODE>rs<CODE> </CODE>sun<TT>)</TT><CODE> </CODE>c<CODE>.</CODE>children<CODE> </CODE><B>then</B><CODE> </CODE><B>true</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><TT>(</TT><CODE> </CODE><B>if</B><CODE> </CODE>c<CODE>.</CODE>listener<CODE> </CODE>rs<CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT>rs<CODE>.</CODE>last<CODE> </CODE><CODE>&lt;-</CODE>c;<CODE> </CODE><B>true</B><TT>)</TT><CODE> </CODE><B>else</B><CODE> </CODE><B>false</B><CODE> </CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><B>false</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>KeyPress<CODE> </CODE><CODE>|</CODE><CODE> </CODE>KeyRelease<CODE> </CODE>-&gt;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>has_key_focus<CODE> </CODE>rs<CODE> </CODE>c<CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE><B>if</B><CODE> </CODE>c<CODE>.</CODE>listener<CODE> </CODE>rs<CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT>rs<CODE>.</CODE>last<CODE>&lt;-</CODE>c;<CODE> </CODE><B>true</B><TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><B>false</B><CODE> </CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE>List.exists<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>sun<CODE> </CODE>-&gt;<CODE> </CODE>send_event<CODE> </CODE>rs<CODE> </CODE>sun<TT>)</TT><CODE> </CODE>c<CODE>.</CODE>children<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE>c<CODE>.</CODE>listener<CODE> </CODE>rs;;<BR><CODE>val send_event : rich_status -&gt; component -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
Note that the hierarchical structure of the components is really a tree 
and not a cyclic graph. This guarantees that the recursion in 
the <TT>send_event</TT> function cannot cause an infinite loop.<BR>
<BR>

<H4> Event Creation</H4>
We differentiate between two kinds of events: those produced by a physical action 
(such as a mouse click) and those which arise from some action linked with the previous
history of the system (such as the movement of the mouse cursor out of the screen area
occupied by a component). As a result we define two functions for creating rich
events.<BR>
<BR>
The function which deals with the former kind constructs a rich event out of two sets
of physical status information:


<PRE><BR># <B>let</B><CODE> </CODE>compute_rich_event<CODE> </CODE>s0<CODE> </CODE>s1<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>s0<CODE>.</CODE>Graphics.button<CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE>s1<CODE>.</CODE>Graphics.button<CODE> </CODE><B>then</B><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>begin</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>s0<CODE>.</CODE>Graphics.button<CODE> </CODE><B>then</B><CODE> </CODE>MouseDown<CODE> </CODE><B>else</B><CODE> </CODE>MouseUp<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><B>if</B><CODE> </CODE>s1<CODE>.</CODE>Graphics.keypressed<CODE> </CODE><B>then</B><CODE> </CODE>KeyPress<CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>s0<CODE>.</CODE>Graphics.mouse_x<CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE>s1<CODE>.</CODE>Graphics.mouse_x<CODE> </CODE><CODE> </CODE><TT>)</TT><CODE> </CODE><CODE>||</CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>s0<CODE>.</CODE>Graphics.mouse_y<CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE>s1<CODE>.</CODE>Graphics.mouse_y<CODE> </CODE><CODE> </CODE><TT>)</TT><CODE> </CODE><B>then</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>begin</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>s1<CODE>.</CODE>Graphics.button<CODE> </CODE><B>then</B><CODE> </CODE>MouseDrag<CODE> </CODE><B>else</B><CODE> </CODE>MouseMove<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE>raise<CODE> </CODE>Not_found;;<BR><CODE>val compute_rich_event : Graphics.status -&gt; Graphics.status -&gt; rich_event =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The function creating the latter kind of event uses the last two rich events:


<PRE><BR># <B>let</B><CODE> </CODE>send_new_events<CODE> </CODE>res0<CODE> </CODE>res1<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>res0<CODE>.</CODE>key_focus<CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE>res1<CODE>.</CODE>key_focus<CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>begin</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<TT>(</TT>send_event<CODE> </CODE><CODE> </CODE>{res1<CODE> </CODE><B>with</B><CODE> </CODE>re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>LostFocus}<CODE> </CODE>res0<CODE>.</CODE>key_focus<TT>)</TT>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<TT>(</TT>send_event<CODE> </CODE><CODE> </CODE>{res1<CODE> </CODE><B>with</B><CODE> </CODE>re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>GotFocus}<CODE> </CODE>res1<CODE>.</CODE>key_focus<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>res0<CODE>.</CODE>last<CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE>res1<CODE>.</CODE>last<TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><TT>(</TT><CODE> </CODE>res1<CODE>.</CODE>re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>MouseMove<TT>)</TT><CODE> </CODE><CODE>||</CODE><CODE> </CODE><TT>(</TT>res1<CODE>.</CODE>re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>MouseDrag<TT>)</TT><TT>)</TT><CODE> </CODE><B>then</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>begin</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<TT>(</TT>send_event<CODE> </CODE><CODE> </CODE>{res1<CODE> </CODE><B>with</B><CODE> </CODE>re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>MouseExit}<CODE> </CODE>res0<CODE>.</CODE>last<TT>)</TT>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<TT>(</TT>send_event<CODE> </CODE><CODE> </CODE>{res1<CODE> </CODE><B>with</B><CODE> </CODE>re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>MouseEnter}<CODE> </CODE>res1<CODE>.</CODE>last<CODE> </CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B>;;<BR><CODE>val send_new_events : rich_status -&gt; rich_status -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We define an initial value for the <I>rich_event</I> type.
This is used to initialize the history of the event loop.


<PRE><BR># <B>let</B><CODE> </CODE>initial_re<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE>re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Exposure;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>stat<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{<CODE> </CODE>Graphics.mouse_x<CODE>=</CODE><CODE>0</CODE>;<CODE> </CODE>Graphics.mouse_y<CODE>=</CODE><CODE>0</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.key<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>' '</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.button<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>false</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.keypressed<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>false</B><CODE> </CODE>};<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>key_focus<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>gen_focus<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>last<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component<CODE> </CODE>}<CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>

<H4> Event Loop</H4>
The event loop manages the sequence of interactions with a component, 
usually the ancestor component for all the components of the interface.
It is supplied with two booleans indicating whether the interface should be 
redisplayed after every physical event has been handled
(<TT>b_disp</TT>) and whether to handle mouse movement
(<TT>b_motion</TT>). The final argument (<TT>c</TT>), is the root of the component
tree.


<PRE><BR># <B>let</B><CODE> </CODE>loop<CODE> </CODE>b_disp<CODE> </CODE>b_motion<CODE> </CODE><CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>res0<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE>initial_re<CODE> </CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>display<CODE> </CODE>c;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>lev<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>[</CODE>Graphics<CODE>.</CODE>Button_down;<CODE> </CODE>Graphics<CODE>.</CODE>Button_up;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics<CODE>.</CODE>Key_pressed<CODE>]</CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>flev<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>if</B><CODE> </CODE>b_motion<CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT>Graphics<CODE>.</CODE>Mouse_motion<TT>)</TT><CODE> </CODE>::<CODE> </CODE>lev<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE>lev<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.wait_next_event<CODE> </CODE>flev<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>res1<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>{!</CODE>res0<CODE> </CODE><B>with</B><CODE> </CODE>stat<CODE> </CODE><CODE>=</CODE><CODE> </CODE>s}<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>res2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{res1<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>compute_rich_event<CODE> </CODE><CODE>!</CODE>res0<CODE>.</CODE>stat<CODE> </CODE>res1<CODE>.</CODE>stat}<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<TT>(</TT>send_event<CODE> </CODE>res2<CODE> </CODE>c<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>send_new_events<CODE> </CODE><CODE>!</CODE>res0<CODE> </CODE>res2;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>res0<CODE> </CODE><CODE>:=</CODE><CODE> </CODE>res2;<CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>b_disp<CODE> </CODE><B>then</B><CODE> </CODE>display<CODE> </CODE>c<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Not_found<CODE> </CODE>-&gt;<CODE> </CODE>()<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>e<CODE> </CODE>-&gt;<CODE> </CODE>raise<CODE> </CODE>e;;<BR><CODE>val loop : bool -&gt; bool -&gt; component -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>

The only way out of this loop is when one of the handling routines raises an exception.<BR>
<BR>

<H4> Test Functions</H4>
We define the following two functions to create by hand status information corresponding to mouse
and keyboard events.


<PRE><BR># <B>let</B><CODE> </CODE>make_click<CODE> </CODE>e<CODE> </CODE>x<CODE> </CODE>y<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>stat<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{Graphics.mouse_x<CODE>=</CODE>x;<CODE> </CODE>Graphics.mouse_y<CODE>=</CODE>y;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.key<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>' '</CODE>;<CODE> </CODE>Graphics.button<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>false</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.keypressed<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>false</B>};<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>key_focus<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>gen_focus<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>last<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component}<BR><CODE> </CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>make_key<CODE> </CODE>e<CODE> </CODE>ch<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{re<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>stat<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{Graphics.mouse_x<CODE>=</CODE><CODE>0</CODE>;<CODE> </CODE>Graphics.mouse_y<CODE>=</CODE><CODE>0</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.key<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c;<CODE> </CODE>Graphics.button<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>false</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.keypressed<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>true</B>};<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>key_focus<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>gen_focus<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>last<CODE> </CODE><CODE>=</CODE><CODE> </CODE>empty_component};;<BR><CODE>val make_click : rich_event -&gt; int -&gt; int -&gt; rich_status = &lt;fun&gt;</CODE><BR><CODE>val make_key : rich_event -&gt; 'a -&gt; char -&gt; rich_status = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We can now simulate the sending of a mouse event to a component for test purposes.<BR>
<BR>
<A NAME="toc169"></A>
<H3> Defining Components</H3>
The various mechanisms for display, coordinate change and, propagating event are
now in place. It remains for us to define some components which are both
useful and easy to use. We can classify components into the following three
categories:
<UL>
<LI>
 simple components which do not handle events, such as text to be displayed;

<LI> simple components which handle events, such as text entry fields;

<LI> containers and their various layout strategies.
</UL>
Values are passed between components, or between a component and the application
by modification of shared data. The sharing is implemented by closures which
contain in their environment the data to be modified. Moreover, as the behavior 
of the component can change as a result of event handling, components also contain
an internal state in the closures of their handling functions. For example the
handling function for an input field has access to text while it is being written.
To this end we implement components in the following manner:
<UL>
<LI>
 define a type to represent the internal state of the component;

<LI> declare functions for the manipulation of this state;

<LI> implement the functions for display, testing whether a coordinate is within the component and
handling events;

<LI> implement the function to create the component, thereby associating those closures with
fields in the component;

<LI> test the component by simulating the arrival of events.
</UL>
Creation functions take a list of options to configure the graphics context.
The calculation of the size of a component when it is created needs to
make use of graphics context of the graphical window in order to determine
the width of the text to be displayed. <BR>
<BR>
We describe the implementation of the following components:
<UL>
<LI>
 simple text (<TT>label</TT>);

<LI> simple container (<TT>panel</TT>);

<LI> simple button (<TT>button</TT>);

<LI> choice among a sequence of strings (<TT>choice</TT>);

<LI> text entry field (<TT>textfield</TT>);

<LI> rich component (<TT>border</TT>).
</UL>
<H4> The Label Component</H4>
<A NAME="comp-label"></A>
The simplest component, called a <I>label</I>, displays a string of characters
on the screen. It does not handle events. We will start by describing the display
function and then the creation function.<BR>
<BR>
Display must take account of the foreground and background colors and the character
font. It is the job of the 
<TT>display_init</TT> function to erase the graphical region of the component,
select the foreground color and position the cursor.
The function <TT>display_label</TT> displays the string passed as a parameter 
immediately after the call to <TT>display_init</TT>.


<PRE><BR># <B>let</B><CODE> </CODE>display_init<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.set_color<CODE> </CODE><TT>(</TT>get_gc_bcol<CODE> </CODE><TT>(</TT>get_gc<CODE> </CODE>c<TT>)</TT><TT>)</TT>;<CODE> </CODE>display_rect<CODE> </CODE><CODE> </CODE>c<CODE> </CODE>();<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>gc<CODE>=</CODE><CODE> </CODE>get_gc<CODE> </CODE>c<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>use_gc<CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>a<CODE>,</CODE>b<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>get_gc_cur<CODE> </CODE>gc<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.moveto<CODE> </CODE><TT>(</TT>c<CODE>.</CODE>x<CODE>+</CODE>a<TT>)</TT><CODE> </CODE><TT>(</TT>c<CODE>.</CODE>y<CODE>+</CODE>b<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>display_label<CODE> </CODE>s<CODE> </CODE>c<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>display_init<CODE> </CODE>c;<CODE> </CODE>Graphics.draw_string<CODE> </CODE>s;;<BR><CODE>val display_init : component -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val display_label : string -&gt; component -&gt; unit -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
As this component is very simple it is not necessary to create any internal state.
Only the function 
<TT>display_label</TT> knows the string to be displayed, which is passed by the 
creation function.


<PRE><BR># <B>let</B><CODE> </CODE>create_label<CODE> </CODE>s<CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>make_default_context<CODE> </CODE>()<CODE> </CODE><B>in</B><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc<CODE> </CODE>gc<CODE> </CODE>lopt;<CODE> </CODE>use_gc<CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>w<CODE>,</CODE>h<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.text_size<CODE> </CODE>s<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>u<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_component<CODE> </CODE>w<CODE> </CODE>h<CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>mem<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>x<CODE> </CODE>-&gt;<CODE> </CODE><B>false</B><TT>)</TT>;<CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>display<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>display_label<CODE> </CODE>s<CODE> </CODE><CODE> </CODE>u;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>info<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE>"Label"</CODE>;<CODE> </CODE>u<CODE>.</CODE>gc<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u;;<BR><CODE>val create_label : string -&gt; (string * opt_val) list -&gt; component = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
If we wish to change the colors of this component, we need to manipulate its graphic
context directly.<BR>
<BR>
The display of <I>label</I> <TT>l1</TT> below is depicted in figure <A HREF="book-ora124.html#fig-label">13.1</A>.


<PRE><BR># <B>let</B><CODE> </CODE>courier_bold_24<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Sopt<CODE> </CODE><CODE>"*courier-bold-r-normal-*24*"</CODE><BR><CODE> </CODE><B>and</B><CODE> </CODE>courier_bold_18<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE>Sopt<CODE> </CODE><CODE>"*courier-bold-r-normal-*18*"</CODE>;;<BR># <B>let</B><CODE> </CODE>l1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_label<CODE> </CODE><CODE>"Login: "</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray1<CODE>]</CODE>;;<BR>

</PRE>
<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora046.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 13.1: Displaying a <I>label</I>.</DIV><BR>

<A NAME="fig-label"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>
<H4> The <I>panel</I> Component, Containers and Layout</H4>
<A NAME="comp-panel"></A>
A <I>panel</I> is a graphical area which can be a container.
The function which creates a panel is very simple. It augments the
general function for creating components with a boolean indicating whether
it is a container. The functions for testing location within the <I>panel</I>
and for display are those assigned by default in the <TT>create_component</TT>
function.


<PRE><BR># <B>let</B><CODE> </CODE>create_panel<CODE> </CODE>b<CODE> </CODE><CODE> </CODE>w<CODE> </CODE>h<CODE> </CODE><CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>u<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_component<CODE> </CODE>w<CODE> </CODE>h<CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>container<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>b;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>info<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><B>if</B><CODE> </CODE>b<CODE> </CODE><B>then</B><CODE> </CODE><CODE>"Panel container"</CODE><CODE> </CODE><B>else</B><CODE> </CODE><CODE>"Panel"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>make_default_context<CODE> </CODE>()<CODE> </CODE><B>in</B><CODE> </CODE>set_gc<CODE> </CODE>gc<CODE> </CODE>lopt;<CODE> </CODE>u<CODE>.</CODE>gc<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u;;<BR><CODE>val create_panel :</CODE><BR><CODE>  bool -&gt; int -&gt; int -&gt; (string * opt_val) list -&gt; component = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The tricky part with containers lies in the 
positioning of their child components. We define two new layout functions:
<TT>center_layout</TT> and <TT>grid_layout</TT>.
The first, <TT>center_layout</TT> places a component at the center of 
a container:


<PRE><BR># <B>let</B><CODE> </CODE>center_layout<CODE> </CODE>c<CODE> </CODE>c1<CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>x<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE>.</CODE>x<CODE> </CODE><CODE>+</CODE><CODE> </CODE><TT>(</TT><TT>(</TT>c<CODE>.</CODE>w<CODE> </CODE><CODE>-</CODE>c1<CODE>.</CODE>w<TT>)</TT><CODE> </CODE><CODE>/</CODE><CODE>2</CODE><TT>)</TT>;<CODE> </CODE>c1<CODE>.</CODE>y<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE><CODE>+</CODE><CODE> </CODE><TT>(</TT><TT>(</TT>c<CODE>.</CODE>h<CODE> </CODE><CODE>-</CODE>c1<CODE>.</CODE>h<TT>)</TT><CODE> </CODE><CODE>/</CODE><CODE>2</CODE><TT>)</TT>;;<BR><CODE>val center_layout : component -&gt; component -&gt; 'a -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The second, <TT>grid_layout</TT> divides a container into a grid where
each box has the same size. The layout options in this case are
<TT>"Col"</TT> for the column number and <TT>"Row"</TT> for the row number.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>grid_layout<CODE> </CODE><TT>(</TT>a<CODE>,</CODE><CODE> </CODE>b<TT>)</TT><CODE> </CODE><CODE> </CODE>c<CODE> </CODE>c1<CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>px<CODE> </CODE><CODE>=</CODE><CODE> </CODE>theInt<CODE> </CODE>lopt<CODE> </CODE><CODE>"Col"</CODE><CODE> </CODE><CODE>0</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>py<CODE> </CODE><CODE>=</CODE><CODE> </CODE>theInt<CODE> </CODE>lopt<CODE> </CODE><CODE>"Row"</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>px<CODE> </CODE><CODE>&gt;=</CODE><CODE> </CODE><CODE>0</CODE><TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>px<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE>a<TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT><CODE> </CODE>py<CODE> </CODE><CODE>&gt;=</CODE><CODE>0</CODE><TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>py<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE>b<TT>)</TT><CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>lw<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c<CODE>.</CODE>w<CODE> </CODE><CODE>/</CODE>a<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>lh<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c<CODE>.</CODE>h<CODE> </CODE><CODE>/</CODE>b<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>c1<CODE>.</CODE>w<CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE>lw<TT>)</TT><CODE> </CODE><CODE>||</CODE><CODE> </CODE><TT>(</TT>c1<CODE>.</CODE>h<CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE>lh<TT>)</TT><CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>failwith<CODE> </CODE><CODE>"grid_placement: too big component"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>x<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE>.</CODE>x<CODE> </CODE><CODE>+</CODE><CODE> </CODE>px<CODE> </CODE><CODE>*</CODE><CODE> </CODE>lw<CODE> </CODE><CODE>+</CODE><CODE> </CODE><TT>(</TT>lw<CODE> </CODE><CODE>-</CODE><CODE> </CODE>c1<CODE>.</CODE>w<TT>)</TT><CODE>/</CODE><CODE>2</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>y<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE><CODE>+</CODE><CODE> </CODE>py<CODE> </CODE><CODE>*</CODE><CODE> </CODE>lh<CODE> </CODE><CODE>+</CODE><CODE> </CODE><TT>(</TT>lh<CODE> </CODE><CODE>-</CODE><CODE> </CODE>c1<CODE>.</CODE>h<TT>)</TT><CODE>/</CODE><CODE>2</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><CODE> </CODE>failwith<CODE> </CODE><CODE>"grid_placement: bad position"</CODE>;;<BR><CODE>val grid_layout :</CODE><BR><CODE>  int * int -&gt; component -&gt; component -&gt; (string * opt_val) list -&gt; unit =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>

It is clearly possible to define more. One can also customize a container by 
changing its layout function (<TT>set_layout</TT>). Figure <A HREF="book-ora124.html#fig-panel">13.2</A> shows a <I>panel</I>, declared as a container, in which two <I>labels</I> have been added and 
which corresponds to the following program:<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora047.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 13.2: A <I>panel</I> component.</DIV><BR>

<A NAME="fig-panel"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>

<PRE><BR># <B>let</B><CODE> </CODE>l2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_label<CODE> </CODE><CODE>"Passwd: "</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray1<CODE>]</CODE><CODE> </CODE>;;<CODE> </CODE><BR># <B>let</B><CODE> </CODE>p1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_panel<CODE> </CODE><B>true</B><CODE> </CODE><CODE>1</CODE><CODE>5</CODE><CODE>0</CODE><CODE> </CODE><CODE>8</CODE><CODE>0</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray2<CODE>]</CODE><CODE> </CODE>;;<BR># set_layout<CODE> </CODE><CODE> </CODE><TT>(</TT>grid_layout<CODE> </CODE><TT>(</TT><CODE>1</CODE><CODE>,</CODE><CODE>2</CODE><TT>)</TT><CODE> </CODE>p1<TT>)</TT><CODE> </CODE>p1;;<BR># add_component<CODE> </CODE>p1<CODE> </CODE>l1<CODE> </CODE><CODE>[</CODE><CODE>"Row"</CODE><CODE>,</CODE><CODE> </CODE>Iopt<CODE> </CODE><CODE>1</CODE><CODE>]</CODE>;;<CODE> </CODE><CODE> </CODE><BR># add_component<CODE> </CODE>p1<CODE> </CODE>l2<CODE> </CODE><CODE>[</CODE><CODE>"Row"</CODE><CODE>,</CODE><CODE> </CODE>Iopt<CODE> </CODE><CODE>0</CODE><CODE>]</CODE>;;<CODE> </CODE><CODE> </CODE><BR>

</PRE>
<BR>
<BR>
Since the components need at least one parent so that they can be integrated
into the interface, and since the 
 <TT>Graphics</TT> library only supports one window, we must define a principle window
which is a container.


<PRE><BR># <B>let</B><CODE> </CODE>open_main_window<CODE> </CODE>w<CODE> </CODE>h<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.close_graph();<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.open_graph<CODE> </CODE><TT>(</TT><CODE>" "</CODE><CODE>^</CODE><TT>(</TT>string_of_int<CODE> </CODE>w<TT>)</TT><CODE>^</CODE><CODE>"x"</CODE><CODE>^</CODE><TT>(</TT>string_of_int<CODE> </CODE>h<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>u<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_component<CODE> </CODE><CODE> </CODE>w<CODE> </CODE>h<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>container<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><B>true</B>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>info<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE>"Main Window"</CODE>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u;;<BR><CODE>val open_main_window : int -&gt; int -&gt; component = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>

<H4> The Button Component</H4>
A <I>button</I> is a component which displays a text in its graphical region
and reacts to mouse clicks which occur there. To support this behavior
it retains a piece of state, a value of type <I>button_state</I>, which contains
the text and the mouse handling function.


<PRE><BR># <B>type</B><CODE> </CODE>button_state<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE>{<CODE> </CODE>txt<CODE> </CODE><CODE>:</CODE><CODE> </CODE>string;<CODE> </CODE><B>mutable</B><CODE> </CODE>action<CODE> </CODE><CODE>:</CODE><CODE> </CODE><CODE> </CODE>button_state<CODE> </CODE>-&gt;<CODE> </CODE>unit<CODE> </CODE>}<CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
The function <TT>create_bs</TT> creates this state. The <TT>set_bs_action</TT> 
function changes the handling function and the function <TT>get_bs_text</TT>
retrieves the text of a button.


<PRE><BR># <B>let</B><CODE> </CODE>create_bs<CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{txt<CODE> </CODE><CODE>=</CODE><CODE> </CODE>s;<CODE> </CODE>action<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>fun</B><CODE> </CODE>x<CODE> </CODE>-&gt;<CODE> </CODE>()}<BR><CODE> </CODE><B>let</B><CODE> </CODE>set_bs_action<CODE> </CODE>bs<CODE> </CODE>f<CODE> </CODE><CODE>=</CODE><CODE> </CODE>bs<CODE>.</CODE>action<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>f<BR><CODE> </CODE><B>let</B><CODE> </CODE>get_bs_text<CODE> </CODE>bs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>bs<CODE>.</CODE>txt;;<BR><CODE>val create_bs : string -&gt; button_state = &lt;fun&gt;</CODE><BR><CODE>val set_bs_action : button_state -&gt; (button_state -&gt; unit) -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val get_bs_text : button_state -&gt; string = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The display function is similar to that used by <I>labels</I> with the exception
that the text derives this time from the state information belonging to the button.
By default the listening function activates the action function when a mouse button
is pressed.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>display_button<CODE> </CODE><CODE> </CODE>c<CODE> </CODE>bs<CODE> </CODE><CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>display_init<CODE> </CODE>c;<CODE> </CODE><CODE> </CODE>Graphics.draw_string<CODE> </CODE><TT>(</TT>get_bs_text<CODE> </CODE>bs<TT>)</TT><BR><CODE> </CODE><B>let</B><CODE> </CODE>listener_button<CODE> </CODE>c<CODE> </CODE>bs<CODE> </CODE><CODE> </CODE>e<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>match</B><CODE> </CODE>get_event<CODE> </CODE>e<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>MouseDown<CODE> </CODE>-&gt;<CODE> </CODE>bs<CODE>.</CODE>action<CODE> </CODE>bs;<CODE> </CODE>c<CODE>.</CODE>display<CODE> </CODE>();<CODE> </CODE><B>true</B><BR><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE><B>false</B>;;<BR><CODE>val display_button : component -&gt; button_state -&gt; unit -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val listener_button : component -&gt; button_state -&gt; rich_status -&gt; bool =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We now have all we need to define the creation function for simple buttons: 


<PRE><BR># <B>let</B><CODE> </CODE>create_button<CODE> </CODE>s<CODE> </CODE>lopt<CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>bs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_bs<CODE> </CODE>s<CODE> </CODE><B>in</B><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>make_default_context<CODE> </CODE>()<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc<CODE> </CODE>gc<CODE> </CODE>lopt;<CODE> </CODE>use_gc<CODE> </CODE>gc;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>w<CODE>,</CODE>h<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.text_size<CODE> </CODE><TT>(</TT>get_bs_text<CODE> </CODE>bs<TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>u<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_component<CODE> </CODE>w<CODE> </CODE>h<CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>display<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>display_button<CODE> </CODE>u<CODE> </CODE>bs;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>listener<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>listener_button<CODE> </CODE>u<CODE> </CODE>bs;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>info<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE>"Button"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>gc<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>,</CODE>bs;;<BR><CODE>val create_button :</CODE><BR><CODE>  string -&gt; (string * opt_val) list -&gt; component * button_state = &lt;fun&gt;</CODE><BR>

</PRE>

This returns a tuple of which the first element is the button which has been created 
and the second is the internal state of the button. The latter value is particularly 
useful if we want to change the action function of the button since the button
state is not accessible via the button function.
<BR>
<BR>
Figure <A HREF="book-ora124.html#fig-button">13.3</A> shows a <I>panel</I> to which a button has been added.
We have associated an action function which displays the string contained by the
button on the standard output.<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora048.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 13.3: Button display and reaction to a mouseclick.</DIV><BR>

<A NAME="fig-button"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>

<PRE><BR># <B>let</B><CODE> </CODE>b<CODE>,</CODE>bs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_button<CODE> </CODE><CODE>"Validation"</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray1<CODE>]</CODE>;;<BR># <B>let</B><CODE> </CODE>p2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_panel<CODE> </CODE><B>true</B><CODE> </CODE><CODE>1</CODE><CODE>5</CODE><CODE>0</CODE><CODE> </CODE><CODE>6</CODE><CODE>0</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray2<CODE>]</CODE>;;<BR># set_bs_action<CODE> </CODE>bs<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>bs<CODE> </CODE>-&gt;<CODE> </CODE>print_string<CODE> </CODE><TT>(</TT><CODE> </CODE><TT>(</TT>get_bs_text<CODE> </CODE>bs<TT>)</TT><CODE>^</CODE><CODE> </CODE><CODE>"..."</CODE><TT>)</TT>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_newline()<TT>)</TT>;;<BR># set_layout<CODE> </CODE><CODE> </CODE><TT>(</TT>center_layout<CODE> </CODE>p2<TT>)</TT><CODE> </CODE>p2;;<BR># add_component<CODE> </CODE>p2<CODE> </CODE>b<CODE> </CODE>[];;<BR>

</PRE>
<BR>
<BR>
In contrast to <I>labels</I>, a button component knows how to react to
a mouse click. To test this feature we send the event ``mouse
click'' to a central position on the <I>panel</I> <TT>p2</TT>, which is occupied
by the button. This causes the action associated with the button to be
carried out:


<PRE><BR># send_event<CODE> </CODE><TT>(</TT>make_click<CODE> </CODE>MouseDown<CODE> </CODE><CODE>7</CODE><CODE>5</CODE><CODE> </CODE><CODE>3</CODE><CODE>0</CODE><TT>)</TT><CODE> </CODE>p2;;<BR><CODE>Validation...</CODE><BR><CODE>- : bool = true</CODE><BR>

</PRE>

and returns the value <TT>true</TT> showing that the event has indeed been
handled.<BR>
<BR>

<H4> The <I>choice</I> Component</H4>
The <I>choice</I> component allows us to select one of the choices offered
using a mouse click. There is always a current choice. A mouse click on
another choice causes the current choice to change and causes an action
to be carried out. We use the same technique we used previously for simple
buttons. We start by defining the state needed by a choice list:


<PRE><BR># <B>type</B><CODE> </CODE>choice_state<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE><B>mutable</B><CODE> </CODE>ind<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<CODE> </CODE>values<CODE> </CODE><CODE>:</CODE><CODE> </CODE>string<CODE> </CODE>array;<CODE> </CODE><B>mutable</B><CODE> </CODE>sep<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>height<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<CODE> </CODE><B>mutable</B><CODE> </CODE>action<CODE> </CODE><CODE>:</CODE><CODE> </CODE>choice_state<CODE> </CODE>-&gt;<CODE> </CODE>unit<CODE> </CODE>}<CODE> </CODE>;;<BR>

</PRE>

The index <TT>ind</TT> shows which string is to be highlighted in the 
list of <TT>values</TT>. The <TT>sep</TT> and <TT>height</TT> fields describe
in pixels the distance between two choices and the height of a choice.
The action function takes an argument of type <I>choice_state</I> as an input and does
its job using the index. <BR>
<BR>
We now define the function to create a set of status information and
the function to change to way it is handled.


<PRE><BR># <B>let</B><CODE> </CODE>create_cs<CODE> </CODE>sa<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{ind<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>0</CODE>;<CODE> </CODE>values<CODE> </CODE><CODE>=</CODE><CODE> </CODE>sa;<CODE> </CODE>sep<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>2</CODE>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>height<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>1</CODE>;<CODE> </CODE>action<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>fun</B><CODE> </CODE>x<CODE> </CODE>-&gt;<CODE> </CODE>()}<BR><CODE> </CODE><B>let</B><CODE> </CODE>set_cs_action<CODE> </CODE>cs<CODE> </CODE>f<CODE> </CODE><CODE>=</CODE><CODE> </CODE>cs<CODE>.</CODE>action<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>f<BR><CODE> </CODE><B>let</B><CODE> </CODE>get_cs_text<CODE> </CODE>cs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>cs<CODE>.</CODE>values<CODE>.</CODE><TT>(</TT>cs<CODE>.</CODE>ind<TT>)</TT>;;<BR><CODE>val create_cs : string array -&gt; choice_state = &lt;fun&gt;</CODE><BR><CODE>val set_cs_action : choice_state -&gt; (choice_state -&gt; unit) -&gt; unit = &lt;fun&gt;</CODE><BR><CODE>val get_cs_text : choice_state -&gt; string = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The display function shows the list of all the possible choices and 
accentuates the current choice in inverse video. The event handling 
function reacts to a release of the mouse button.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>display_choice<CODE> </CODE>c<CODE> </CODE>cs<CODE> </CODE><CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>display_init<CODE> </CODE>c;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>x<CODE>,</CODE>y<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.current_point()<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>nb<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Array.length<CODE> </CODE>cs<CODE>.</CODE>values<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>for</B><CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>to</B><CODE> </CODE>nb<CODE>-</CODE><CODE>1</CODE><CODE> </CODE><B>do</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.moveto<CODE> </CODE>x<CODE> </CODE><TT>(</TT>y<CODE> </CODE><CODE>+</CODE><CODE> </CODE>i<CODE>*</CODE><TT>(</TT>cs<CODE>.</CODE>height<CODE>+</CODE><CODE> </CODE>cs<CODE>.</CODE>sep<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.draw_string<CODE> </CODE>cs<CODE>.</CODE>values<CODE>.</CODE><TT>(</TT>i<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.set_color<CODE> </CODE><TT>(</TT>get_gc_fcol<CODE> </CODE><TT>(</TT>get_gc<CODE> </CODE>c<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.fill_rect<CODE> </CODE>x<CODE> </CODE><TT>(</TT>y<CODE>+</CODE><CODE> </CODE>cs<CODE>.</CODE>ind<CODE>*</CODE><TT>(</TT>cs<CODE>.</CODE>height<CODE>+</CODE><CODE> </CODE>cs<CODE>.</CODE>sep<TT>)</TT><TT>)</TT><CODE> </CODE>c<CODE>.</CODE>w<CODE> </CODE>cs<CODE>.</CODE>height;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.set_color<CODE> </CODE><TT>(</TT>get_gc_bcol<CODE> </CODE><TT>(</TT>get_gc<CODE> </CODE>c<TT>)</TT><TT>)</TT>;<CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.moveto<CODE> </CODE>x<CODE> </CODE><CODE> </CODE><TT>(</TT>y<CODE> </CODE><CODE>+</CODE><CODE> </CODE>cs<CODE>.</CODE>ind<CODE>*</CODE><TT>(</TT>cs<CODE>.</CODE>height<CODE> </CODE><CODE>+</CODE><CODE> </CODE>cs<CODE>.</CODE>sep<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.draw_string<CODE> </CODE>cs<CODE>.</CODE>values<CODE>.</CODE><TT>(</TT>cs<CODE>.</CODE>ind<TT>)</TT><CODE> </CODE>;;<BR><CODE>val display_choice : component -&gt; choice_state -&gt; unit -&gt; unit = &lt;fun&gt;</CODE><BR><BR># <B>let</B><CODE> </CODE>listener_choice<CODE> </CODE>c<CODE> </CODE>cs<CODE> </CODE>e<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>match</B><CODE> </CODE>e<CODE>.</CODE>re<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>MouseUp<CODE> </CODE>-&gt;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>x<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>stat<CODE>.</CODE>Graphics.mouse_x<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>y<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e<CODE>.</CODE>stat<CODE>.</CODE>Graphics.mouse_y<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>cy<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>y<CODE> </CODE><CODE>-</CODE><CODE> </CODE>cy<TT>)</TT><CODE> </CODE><CODE>/</CODE><CODE> </CODE><TT>(</TT><CODE> </CODE>cs<CODE>.</CODE>height<CODE> </CODE><CODE>+</CODE><CODE> </CODE>cs<CODE>.</CODE>sep<TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>cs<CODE>.</CODE>ind<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>i;<CODE> </CODE>c<CODE>.</CODE>display<CODE> </CODE>();<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>cs<CODE>.</CODE>action<CODE> </CODE>cs;<CODE> </CODE><B>true</B><BR><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE><B>false</B><CODE> </CODE>;;<BR><CODE>val listener_choice : component -&gt; choice_state -&gt; rich_status -&gt; bool =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
To create a list of possible choices we take a list of strings
and a list of options, and we return the component itself along
with its internal state.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>create_choice<CODE> </CODE>lc<CODE> </CODE>lopt<CODE> </CODE><CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sa<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>Array.of_list<CODE> </CODE><TT>(</TT>List.rev<CODE> </CODE>lc<TT>)</TT><TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>cs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_cs<CODE> </CODE>sa<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>make_default_context<CODE> </CODE>()<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc<CODE> </CODE>gc<CODE> </CODE>lopt;<CODE> </CODE><CODE> </CODE>use_gc<CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>awh<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Array.map<CODE> </CODE><TT>(</TT>Graphics.text_size<TT>)</TT><CODE> </CODE>cs<CODE>.</CODE>values<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>w<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Array.fold_right<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE><TT>(</TT>x<CODE>,</CODE>y<TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>max<CODE> </CODE>x<TT>)</TT><CODE> </CODE><CODE> </CODE>awh<CODE> </CODE><CODE>0</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>h<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Array.fold_right<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE><TT>(</TT>x<CODE>,</CODE>y<TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>max<CODE> </CODE>y<TT>)</TT><CODE> </CODE><CODE> </CODE>awh<CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>h1<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>h<CODE>+</CODE>cs<CODE>.</CODE>sep<TT>)</TT><CODE> </CODE><CODE>*</CODE><CODE> </CODE><TT>(</TT>Array.length<CODE> </CODE>sa<TT>)</TT><CODE> </CODE><CODE>+</CODE><CODE> </CODE>cs<CODE>.</CODE>sep<CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>cs<CODE>.</CODE>height<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>h;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>u<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_component<CODE> </CODE>w<CODE> </CODE>h1<CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>display<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>display_choice<CODE> </CODE>u<CODE> </CODE>cs;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>listener<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>listener_choice<CODE> </CODE>u<CODE> </CODE>cs<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>info<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE>"Choice "</CODE><CODE>^</CODE><CODE> </CODE><TT>(</TT>string_of_int<CODE> </CODE><TT>(</TT>Array.length<CODE> </CODE>cs<CODE>.</CODE>values<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>gc<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>,</CODE>cs;;<BR><CODE>val create_choice :</CODE><BR><CODE>  string list -&gt; (string * opt_val) list -&gt; component * choice_state = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The sequence of three pictures in figure
<A HREF="book-ora124.html#fig-choice">13.4</A> shows a <I>panel</I>
to which a list of choices has been added. To it we have bound an
action function which displays the chosen string to the standard output.
The pictures arise from mouse clicks simulated by the following program.
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=1>
<TR><TD  ALIGN=center NOWRAP><IMG SRC="book-ora049.gif"></TD>
<TD  ALIGN=center NOWRAP><IMG SRC="book-ora050.gif"></TD>
<TD  ALIGN=center NOWRAP><IMG SRC="book-ora051.gif"></TD>
</TR></TABLE>
<BR>
<DIV ALIGN=center>Figure 13.4: Displaying and selecting from a choice list.</DIV><BR>

<A NAME="fig-choice"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>


<PRE><BR># <B>let</B><CODE> </CODE>c<CODE>,</CODE>cs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_choice<CODE> </CODE><CODE>[</CODE><CODE>"Helium"</CODE>;<CODE> </CODE><CODE>"Gallium"</CODE>;<CODE> </CODE><CODE>"Pentium"</CODE><CODE>]</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray1<CODE>]</CODE>;;<BR># <B>let</B><CODE> </CODE>p3<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_panel<CODE> </CODE><B>true</B><CODE> </CODE><CODE>1</CODE><CODE>1</CODE><CODE>0</CODE><CODE> </CODE><CODE>1</CODE><CODE>1</CODE><CODE>0</CODE><CODE> </CODE><CODE> </CODE><CODE>[</CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray2<CODE>]</CODE>;;<BR># set_cs_action<CODE> </CODE>cs<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>cs<CODE> </CODE>-&gt;<CODE> </CODE>print_string<CODE> </CODE><TT>(</TT><CODE> </CODE><TT>(</TT>get_cs_text<CODE> </CODE>cs<TT>)</TT><CODE>^</CODE><CODE>"..."</CODE><TT>)</TT>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_newline()<TT>)</TT>;;<BR># set_layout<CODE> </CODE><CODE> </CODE><TT>(</TT>center_layout<CODE> </CODE>p3<TT>)</TT><CODE> </CODE>p3;;<BR># add_component<CODE> </CODE>p3<CODE> </CODE>c<CODE> </CODE>[];;<BR>

</PRE>
<BR>
<BR>
Here also we can test the component straight away by sending several 
events. The following changes the selection, as is shown in the central 
picture in figure
<A HREF="book-ora124.html#fig-choice">13.4</A>. 


<PRE><BR># send_event<CODE> </CODE><TT>(</TT>make_click<CODE> </CODE>MouseUp<CODE> </CODE><CODE>6</CODE><CODE>0</CODE><CODE> </CODE><CODE>5</CODE><CODE>5</CODE><CODE> </CODE><TT>)</TT><CODE> </CODE>p3;;<BR><CODE>Gallium...</CODE><BR><CODE>- : bool = true</CODE><BR>

</PRE>
<BR>
<BR>
The sending of the following event selects the first element in the choice list


<PRE><BR># send_event<CODE> </CODE><TT>(</TT>make_click<CODE> </CODE>MouseUp<CODE> </CODE><CODE>6</CODE><CODE>0</CODE><CODE> </CODE><CODE>9</CODE><CODE>0</CODE><CODE> </CODE><TT>)</TT><CODE> </CODE>p3;;<BR><CODE>Helium...</CODE><BR><CODE>- : bool = true</CODE><BR>

</PRE>
<BR>
<BR>

<H4> The <I>textfield</I> Component</H4>
The text input field, or 
<I>textfield</I>, is an area which enables us to input a text string.
The text can be aligned to the left or (typically for a calculator) the right. 
Furthermore a cursor shows where the next character will be entered.
Here we need a more complex internal state. This includes the text which is
being entered, the direction of the justification, a description of the 
cursor, a description of how the characters are displayed and the action function.


<PRE><BR># <B>type</B><CODE> </CODE>textfield_state<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE>txt<CODE> </CODE><CODE>:</CODE><CODE> </CODE>string;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>dir<CODE> </CODE><CODE>:</CODE><CODE> </CODE>bool;<CODE> </CODE><B>mutable</B><CODE> </CODE>ind1<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<CODE> </CODE><B>mutable</B><CODE> </CODE>ind2<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<CODE> </CODE>len<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>visible_cursor<CODE> </CODE><CODE>:</CODE><CODE> </CODE>bool;<CODE> </CODE><B>mutable</B><CODE> </CODE>cursor<CODE> </CODE><CODE>:</CODE><CODE> </CODE>char;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>visible_echo<CODE> </CODE><CODE>:</CODE><CODE> </CODE>bool;<CODE> </CODE><B>mutable</B><CODE> </CODE>echo<CODE> </CODE><CODE>:</CODE><CODE> </CODE>char;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>action<CODE> </CODE><CODE>:</CODE><CODE> </CODE>textfield_state<CODE> </CODE>-&gt;<CODE> </CODE>unit<CODE> </CODE>}<CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
To create this internal state we need the initial text, the number of 
characters available for the text input field and the justification 
of the text.


<PRE><BR># <B>let</B><CODE> </CODE>create_tfs<CODE> </CODE>txt<CODE> </CODE>size<CODE> </CODE>dir<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>l<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.length<CODE> </CODE>txt<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><B>if</B><CODE> </CODE>size<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE>l<CODE> </CODE><B>then</B><CODE> </CODE>failwith<CODE> </CODE><CODE>"create_tfs"</CODE><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ind1<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>if</B><CODE> </CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>else</B><CODE> </CODE>size<CODE>-</CODE><CODE>1</CODE><CODE>-</CODE>l<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>ind2<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>if</B><CODE> </CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE>l<CODE> </CODE><B>else</B><CODE> </CODE>size<CODE>-</CODE><CODE>1</CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>n_txt<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT><B>if</B><CODE> </CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT>txt<CODE>^</CODE><TT>(</TT>String.make<CODE> </CODE><TT>(</TT>size<CODE>-</CODE>l<TT>)</TT><CODE> </CODE><CODE>' '</CODE><TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><TT>(</TT><TT>(</TT>String.make<CODE> </CODE><TT>(</TT>size<CODE>-</CODE>l<TT>)</TT><CODE> </CODE><CODE>' '</CODE><TT>)</TT><CODE>^</CODE>txt<CODE> </CODE><TT>)</TT><TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{txt<CODE> </CODE><CODE>=</CODE><CODE> </CODE>n_txt;<CODE> </CODE>dir<CODE>=</CODE>dir;<CODE> </CODE>ind1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ind1;<CODE> </CODE>ind2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ind2;<CODE> </CODE>len<CODE>=</CODE>size;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>visible_cursor<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>false</B>;<CODE> </CODE><CODE> </CODE>cursor<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>' '</CODE>;<CODE> </CODE>visible_echo<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE><B>true</B>;<CODE> </CODE>echo<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>' '</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>action<CODE>=</CODE><CODE> </CODE><B>fun</B><CODE> </CODE>x<CODE> </CODE>-&gt;<CODE> </CODE>()};;<BR><CODE>val create_tfs : string -&gt; int -&gt; bool -&gt; textfield_state = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The following functions allow us to access various fields, including the
displayed text.


<PRE><BR># <B>let</B><CODE> </CODE>set_tfs_action<CODE> </CODE>tfs<CODE> </CODE>f<CODE> </CODE><CODE>=</CODE><CODE> </CODE>tfs<CODE>.</CODE>action<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>f<BR><CODE> </CODE><B>let</B><CODE> </CODE>set_tfs_cursor<CODE> </CODE>b<CODE> </CODE>c<CODE> </CODE>tfs<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE>tfs<CODE>.</CODE>visible_cursor<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>b;<CODE> </CODE>tfs<CODE>.</CODE>cursor<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>set_tfs_echo<CODE> </CODE>b<CODE> </CODE>c<CODE> </CODE>tfs<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE>tfs<CODE>.</CODE>visible_echo<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>b;<CODE> </CODE>tfs<CODE>.</CODE>echo<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>c<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>get_tfs_text<CODE> </CODE>tfs<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>tfs<CODE>.</CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE>String.sub<CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE>tfs<CODE>.</CODE>ind1<CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>-</CODE><CODE> </CODE>tfs<CODE>.</CODE>ind1<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE>String.sub<CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind1<CODE>+</CODE><CODE>1</CODE><TT>)</TT><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>-</CODE><CODE> </CODE>tfs<CODE>.</CODE>ind1<TT>)</TT>;;<BR>

</PRE>
<BR>
<BR>
The <TT>set_tfs_text</TT> function changes the text within the internal state
<TT>tfs</TT> of the component <TT>tf</TT> with the string <TT>txt</TT>.


<PRE><BR># <B>let</B><CODE> </CODE>set_tfs_text<CODE> </CODE>tf<CODE> </CODE>tfs<CODE> </CODE>txt<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>l<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.length<CODE> </CODE>txt<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>l<CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE>tfs<CODE>.</CODE>len<CODE> </CODE><B>then</B><CODE> </CODE>failwith<CODE> </CODE><CODE>"set_tfs_text"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>String.blit<CODE> </CODE><CODE> </CODE><TT>(</TT>String.make<CODE> </CODE>tfs<CODE>.</CODE>len<CODE> </CODE><CODE>' '</CODE><TT>)</TT><CODE> </CODE><CODE>0</CODE><CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE><CODE>0</CODE><CODE> </CODE>tfs<CODE>.</CODE>len;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>tfs<CODE>.</CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT>String.blit<CODE> </CODE>txt<CODE> </CODE><CODE>0</CODE><CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE><CODE>0</CODE><CODE> </CODE>l;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>l<CODE> </CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE>String.blit<CODE> </CODE>txt<CODE> </CODE><CODE>0</CODE><CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>len<CODE> </CODE><CODE>-</CODE>l<TT>)</TT><CODE> </CODE>l;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>tfs<CODE>.</CODE>ind1<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>tfs<CODE>.</CODE>len<CODE>-</CODE>l<CODE>-</CODE><CODE>1</CODE><CODE> </CODE><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>tf<CODE>.</CODE>display<CODE> </CODE>();;<CODE> </CODE><BR><CODE>val set_tfs_text : component -&gt; textfield_state -&gt; string -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
Display operations must take account of how the character is echoed and
the visibility of the cursor. The <TT>display_textfield</TT> function
calls the <TT>display_cursor</TT> function which shows where the
cursor is.


<PRE><BR># <B>let</B><CODE> </CODE>display_cursor<CODE> </CODE>c<CODE> </CODE>tfs<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>tfs<CODE>.</CODE>visible_cursor<CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE>use_gc<CODE> </CODE><TT>(</TT>get_gc<CODE> </CODE>c<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>x<CODE>,</CODE>y<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.current_point()<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>a<CODE>,</CODE>b<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.text_size<CODE> </CODE><CODE>" "</CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>shift<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE>a<CODE> </CODE><CODE>*</CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><B>if</B><CODE> </CODE>tfs<CODE>.</CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE>max<CODE> </CODE><TT>(</TT>min<CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>len<CODE>-</CODE><CODE>1</CODE><TT>)</TT><CODE> </CODE>tfs<CODE>.</CODE>ind2<TT>)</TT><CODE> </CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE>tfs<CODE>.</CODE>ind2<TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.moveto<CODE> </CODE><TT>(</TT>c<CODE>.</CODE>x<CODE>+</CODE>x<CODE> </CODE><CODE>+</CODE><CODE> </CODE>shift<TT>)</TT><CODE> </CODE><TT>(</TT>c<CODE>.</CODE>y<CODE>+</CODE>y<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.draw_char<CODE> </CODE>tfs<CODE>.</CODE>cursor<TT>)</TT>;;<BR><CODE>val display_cursor : component -&gt; textfield_state -&gt; unit = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>display_textfield<CODE> </CODE>c<CODE> </CODE>tfs<CODE> </CODE><CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>display_init<CODE> </CODE>c;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.make<CODE> </CODE>tfs<CODE>.</CODE>len<CODE> </CODE><CODE>' '</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>txt<CODE> </CODE><CODE>=</CODE><CODE> </CODE>get_tfs_text<CODE> </CODE>tfs<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>nl<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.length<CODE> </CODE>txt<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind1<CODE> </CODE><CODE>&gt;=</CODE><CODE> </CODE><CODE>0</CODE><TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>not<CODE> </CODE>tfs<CODE>.</CODE>dir<TT>)</TT><CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.draw_string<CODE> </CODE><TT>(</TT>String.sub<CODE> </CODE>s<CODE> </CODE><CODE>0</CODE><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind1<CODE>+</CODE><CODE>1</CODE><TT>)</TT><CODE> </CODE><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>tfs<CODE>.</CODE>visible_echo<CODE> </CODE><CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT>Graphics.draw_string<CODE> </CODE><TT>(</TT>get_tfs_text<CODE> </CODE>tfs<TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE>Graphics.draw_string<CODE> </CODE><TT>(</TT>String.make<CODE> </CODE><TT>(</TT>String.length<CODE> </CODE>txt<TT>)</TT><CODE> </CODE>tfs<CODE>.</CODE>echo<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>nl<CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE>tfs<CODE>.</CODE>ind2<TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>dir<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>then</B><CODE> </CODE>Graphics.draw_string<CODE> </CODE><TT>(</TT>String.sub<CODE> </CODE>s<CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE> </CODE><TT>(</TT>nl<CODE>-</CODE>tfs<CODE>.</CODE>ind2<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>display_cursor<CODE> </CODE>c<CODE> </CODE>tfs;;<BR><CODE>val display_textfield : component -&gt; textfield_state -&gt; unit -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The event-listener function for this kind of component is more complex.
According to the input direction (left or right justified) 
we may need to move the string which has already been input.
Capture of focus is achieved by a mouse click in the input zone.


<PRE><BR># <B>let</B><CODE> </CODE>listener_text_field<CODE> </CODE>u<CODE> </CODE>tfs<CODE> </CODE>e<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>e<CODE>.</CODE>re<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>MouseDown<CODE> </CODE>-&gt;<CODE> </CODE>take_key_focus<CODE> </CODE>e<CODE> </CODE>u<CODE> </CODE>;<CODE> </CODE><B>true</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>KeyPress<CODE> </CODE>-&gt;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE><B>if</B><CODE> </CODE>Char.code<CODE> </CODE><TT>(</TT>get_key<CODE> </CODE>e<TT>)</TT><CODE> </CODE><CODE> </CODE><CODE>&gt;=</CODE><CODE> </CODE><CODE>3</CODE><CODE>2</CODE><CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>begin</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE><B>if</B><CODE> </CODE>tfs<CODE>.</CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE><TT>(</TT><CODE> </CODE><B>if</B><CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>&gt;=</CODE><CODE> </CODE>tfs<CODE>.</CODE>len<CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>String.blit<CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE><CODE>1</CODE><CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE><CODE>0</CODE><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind2<CODE>-</CODE><CODE>1</CODE><TT>)</TT>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE>-</CODE><CODE>1</CODE><TT>)</TT><CODE> </CODE><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>tfs<CODE>.</CODE>txt<CODE>.[</CODE>tfs<CODE>.</CODE>ind2<CODE>]</CODE><CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>get_key<CODE> </CODE>e;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>+</CODE><CODE>1</CODE><CODE> </CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE>String.blit<CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE><CODE>1</CODE><CODE> </CODE>tfs<CODE>.</CODE>txt<CODE> </CODE><CODE>0</CODE><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind2<TT>)</TT>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>tfs<CODE>.</CODE>txt<CODE>.[</CODE>tfs<CODE>.</CODE>ind2<CODE>]</CODE><CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>get_key<CODE> </CODE>e;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>tfs<CODE>.</CODE>ind1<CODE> </CODE><CODE>&gt;=</CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE><B>then</B><CODE> </CODE>tfs<CODE>.</CODE>ind1<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>tfs<CODE>.</CODE>ind1<CODE> </CODE><CODE>-</CODE><CODE>1</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>)</TT>;<CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><TT>(</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT><CODE> </CODE><B>match</B><CODE> </CODE>Char.code<CODE> </CODE><TT>(</TT>get_key<CODE> </CODE>e<TT>)</TT><CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>1</CODE><CODE>3</CODE><CODE> </CODE>-&gt;<CODE> </CODE>tfs<CODE>.</CODE>action<CODE> </CODE>tfs<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE> </CODE><CODE>9</CODE><CODE> </CODE>-&gt;<CODE> </CODE>lose_key_focus<CODE> </CODE>e<CODE> </CODE>u<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE> </CODE><CODE>8</CODE><CODE> </CODE>-&gt;<CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>dir<CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE><CODE>0</CODE><TT>)</TT><TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>then</B><CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>tfs<CODE>.</CODE>ind2<CODE> </CODE><CODE>-</CODE><CODE>1</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><B>if</B><CODE> </CODE><TT>(</TT>not<CODE> </CODE>tfs<CODE>.</CODE>dir<TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>ind1<CODE> </CODE><CODE>&lt;</CODE><CODE> </CODE>tfs<CODE>.</CODE>len<CODE> </CODE><CODE>-</CODE><CODE>1</CODE><TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>then</B><CODE> </CODE>tfs<CODE>.</CODE>ind1<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>tfs<CODE>.</CODE>ind1<CODE>+</CODE><CODE>1</CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE>()<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>)</TT><TT>)</TT><TT>)</TT>;<CODE> </CODE>u<CODE>.</CODE>display();<CODE> </CODE><B>true</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE><B>false</B>;;<BR><CODE>val listener_text_field :</CODE><BR><CODE>  component -&gt; textfield_state -&gt; rich_status -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The function which creates text entry fields repeats the same pattern we have 
seen in the previous components.


<PRE><BR># <B>let</B><CODE> </CODE>create_text_field<CODE> </CODE><CODE> </CODE>txt<CODE> </CODE>size<CODE> </CODE>dir<CODE> </CODE>lopt<CODE> </CODE><CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>tfs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_tfs<CODE> </CODE>txt<CODE> </CODE>size<CODE> </CODE>dir<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>l<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.length<CODE> </CODE>txt<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>make_default_context<CODE> </CODE>()<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc<CODE> </CODE>gc<CODE> </CODE>lopt;<CODE> </CODE>use_gc<CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>w<CODE>,</CODE>h<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.text_size<CODE> </CODE><TT>(</TT>tfs<CODE>.</CODE>txt<TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>u<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_component<CODE> </CODE>w<CODE> </CODE>h<CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>display<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>display_textfield<CODE> </CODE>u<CODE> </CODE>tfs;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>listener<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE> </CODE>listener_text_field<CODE> </CODE>u<CODE> </CODE>tfs<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>.</CODE>info<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><CODE>"TextField"</CODE>;<CODE> </CODE>u<CODE>.</CODE>gc<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>gc;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>u<CODE>,</CODE>tfs;;<BR><CODE>val create_text_field :</CODE><BR><CODE>  string -&gt;</CODE><BR><CODE>  int -&gt; bool -&gt; (string * opt_val) list -&gt; component * textfield_state =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
This function returns a tuple consisting of the component itself, and the
internal state of that component. We can test the creation of the component
in figure <A HREF="book-ora124.html#fig-textfield">13.5</A> as follows:


<PRE><BR># <B>let</B><CODE> </CODE>tf1<CODE>,</CODE>tfs1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_text_field<CODE> </CODE><CODE>"jack"</CODE><CODE> </CODE><CODE> </CODE><CODE>8</CODE><CODE> </CODE><B>true</B><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24<CODE>]</CODE>;;<BR># <B>let</B><CODE> </CODE>tf2<CODE>,</CODE>tfs2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_text_field<CODE> </CODE><CODE>"koala"</CODE><CODE> </CODE><CODE>8</CODE><CODE> </CODE><CODE> </CODE><B>false</B><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24<CODE>]</CODE>;;<BR># set_tfs_cursor<CODE> </CODE><B>true</B><CODE> </CODE><CODE>'_'</CODE><CODE> </CODE>tfs1;;<BR># set_tfs_cursor<CODE> </CODE><B>true</B><CODE> </CODE><CODE>'_'</CODE><CODE> </CODE>tfs2;;<BR># set_tfs_echo<CODE> </CODE><B>false</B><CODE> </CODE><CODE>'*'</CODE><CODE> </CODE>tfs2;;<BR># <B>let</B><CODE> </CODE>p4<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_panel<CODE> </CODE><B>true</B><CODE> </CODE><CODE>1</CODE><CODE>4</CODE><CODE>0</CODE><CODE> </CODE><CODE>8</CODE><CODE>0</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray2<CODE>]</CODE>;;<BR># set_layout<CODE> </CODE><CODE> </CODE><TT>(</TT>grid_layout<CODE> </CODE><TT>(</TT><CODE>1</CODE><CODE>,</CODE><CODE>2</CODE><TT>)</TT><CODE> </CODE>p4<TT>)</TT><CODE> </CODE>p4;;<BR># add_component<CODE> </CODE>p4<CODE> </CODE>tf1<CODE> </CODE><CODE>[</CODE><CODE>"Row"</CODE><CODE>,</CODE><CODE> </CODE>Iopt<CODE> </CODE><CODE>1</CODE><CODE>]</CODE>;;<CODE> </CODE><CODE> </CODE><BR># add_component<CODE> </CODE>p4<CODE> </CODE>tf2<CODE> </CODE><CODE>[</CODE><CODE>"Row"</CODE><CODE>,</CODE><CODE> </CODE>Iopt<CODE> </CODE><CODE>0</CODE><CODE>]</CODE>;;<CODE> </CODE><CODE> </CODE><BR>

</PRE>
<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora052.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 13.5: Text input component.</DIV><BR>

<A NAME="fig-textfield"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE><A NAME="toc170"></A>
<H3> Enriched Components</H3>
Beyond the components described so far, it is also possible to construct
new ones, for example components with bevelled edges such as those in the 
calculator on page <A HREF="book-ora051.html#sec-calc-vg">??</A>. 
To create this effect we construct a <I>panel</I> 
larger than the component, fill it out in a certain way and
add the required component to the center. 


<PRE><BR># <B>type</B><CODE> </CODE>border_state<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<B>mutable</B><CODE> </CODE>relief<CODE> </CODE><CODE>:</CODE><CODE> </CODE>string;<CODE> </CODE><B>mutable</B><CODE> </CODE>line<CODE> </CODE><CODE>:</CODE><CODE> </CODE>bool;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>mutable</B><CODE> </CODE>bg2<CODE> </CODE><CODE>:</CODE><CODE> </CODE>Graphics.color;<CODE> </CODE><B>mutable</B><CODE> </CODE>size<CODE> </CODE><CODE>:</CODE><CODE> </CODE>int};;<BR>

</PRE>
<BR>
<BR>
The creation function takes a list of options and constructs an internal state.


<PRE><BR># <B>let</B><CODE> </CODE>create_border_state<CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{relief<CODE> </CODE><CODE>=</CODE><CODE> </CODE>theString<CODE> </CODE>lopt<CODE> </CODE><CODE>"Relief"</CODE><CODE> </CODE><CODE>"Flat"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>line<CODE> </CODE><CODE>=</CODE><CODE> </CODE>theBool<CODE> </CODE>lopt<CODE> </CODE><CODE>"Outlined"</CODE><CODE> </CODE><B>false</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>bg2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>theColor<CODE> </CODE>lopt<CODE> </CODE><CODE>"Background2"</CODE><CODE> </CODE>Graphics.black;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>size<CODE> </CODE><CODE>=</CODE><CODE> </CODE>theInt<CODE> </CODE>lopt<CODE> </CODE><CODE>"Border_size"</CODE><CODE> </CODE><CODE>2</CODE>};;<BR><CODE>val create_border_state : (string * opt_val) list -&gt; border_state = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We define the profile of the border used in the boxes of figure
<A HREF="book-ora048.html#fig-boites2">5.6</A> (page <A HREF="book-ora048.html#fig-boites2">??</A>)
by defining the options <TT>"Top"</TT>, <TT>"Bot"</TT> and <TT>"Flat"</TT>.


<PRE><BR># <B>let</B><CODE> </CODE>display_border<CODE> </CODE>bs<CODE> </CODE>c1<CODE> </CODE>c<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>x1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c<CODE>.</CODE>x<CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>y1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c<CODE>.</CODE>y<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>x2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>x1<CODE>+</CODE>c<CODE>.</CODE>w<CODE>-</CODE><CODE>1</CODE><CODE> </CODE><B>and</B><CODE> </CODE>y2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>y1<CODE>+</CODE>c<CODE>.</CODE>h<CODE>-</CODE><CODE>1</CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ix1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>c1<CODE>.</CODE>x<CODE> </CODE><B>and</B><CODE> </CODE>iy1<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE>c1<CODE>.</CODE>y<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ix2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ix1<CODE>+</CODE>c1<CODE>.</CODE>w<CODE>-</CODE><CODE>1</CODE><CODE> </CODE><B>and</B><CODE> </CODE>iy2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>iy1<CODE>+</CODE>c1<CODE>.</CODE>h<CODE>-</CODE><CODE>1</CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>border1<CODE> </CODE>g<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Graphics.set_color<CODE> </CODE>g;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.fill_poly<CODE> </CODE><CODE>[|</CODE><CODE> </CODE><TT>(</TT>x1<CODE>,</CODE>y1<TT>)</TT>;<TT>(</TT>ix1<CODE>,</CODE>iy1<TT>)</TT>;<TT>(</TT>ix2<CODE>,</CODE>iy1<TT>)</TT>;<TT>(</TT>x2<CODE>,</CODE>y1<TT>)</TT><CODE> </CODE><CODE>|]</CODE><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.fill_poly<CODE> </CODE><CODE>[|</CODE><CODE> </CODE><TT>(</TT>x2<CODE>,</CODE>y1<TT>)</TT>;<TT>(</TT>ix2<CODE>,</CODE>iy1<TT>)</TT>;<TT>(</TT>ix2<CODE>,</CODE>iy2<TT>)</TT>;<TT>(</TT>x2<CODE>,</CODE>y2<TT>)</TT><CODE> </CODE><CODE>|]</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>border2<CODE> </CODE>g<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE>Graphics.set_color<CODE> </CODE>g;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.fill_poly<CODE> </CODE><CODE>[|</CODE><CODE> </CODE><TT>(</TT>x1<CODE>,</CODE>y2<TT>)</TT>;<TT>(</TT>ix1<CODE>,</CODE>iy2<TT>)</TT>;<TT>(</TT>ix2<CODE>,</CODE>iy2<TT>)</TT>;<TT>(</TT>x2<CODE>,</CODE>y2<TT>)</TT><CODE> </CODE><CODE>|]</CODE><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Graphics.fill_poly<CODE> </CODE><CODE>[|</CODE><CODE> </CODE><TT>(</TT>x1<CODE>,</CODE>y1<TT>)</TT>;<TT>(</TT>ix1<CODE>,</CODE>iy1<TT>)</TT>;<TT>(</TT>ix1<CODE>,</CODE>iy2<TT>)</TT>;<TT>(</TT>x1<CODE>,</CODE>y2<TT>)</TT><CODE> </CODE><CODE>|]</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>display_rect<CODE> </CODE>c<CODE> </CODE>();<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>bs<CODE>.</CODE>line<CODE> </CODE><B>then</B><CODE> </CODE><TT>(</TT>Graphics.set_color<CODE> </CODE><TT>(</TT>get_gc_fcol<CODE> </CODE><TT>(</TT>get_gc<CODE> </CODE>c<TT>)</TT><TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>draw_rect<CODE> </CODE>x1<CODE> </CODE>y1<CODE> </CODE>c<CODE>.</CODE>w<CODE> </CODE>c<CODE>.</CODE>h<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>b1_col<CODE> </CODE><CODE>=</CODE><CODE> </CODE>get_gc_bcol<CODE> </CODE><TT>(</TT><CODE> </CODE>get_gc<CODE> </CODE>c<TT>)</TT><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>b2_col<CODE> </CODE><CODE>=</CODE><CODE> </CODE>bs<CODE>.</CODE>bg2<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>bs<CODE>.</CODE>relief<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Top"</CODE><CODE> </CODE>-&gt;<CODE> </CODE><CODE> </CODE><TT>(</TT>border1<CODE> </CODE>b1_col;<CODE> </CODE>border2<CODE> </CODE>b2_col<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE> </CODE><CODE>"Bot"</CODE><CODE> </CODE>-&gt;<CODE> </CODE><TT>(</TT>border1<CODE> </CODE>b2_col;<CODE> </CODE>border2<CODE> </CODE>b1_col<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE> </CODE><CODE>"Flat"</CODE><CODE> </CODE>-&gt;<CODE> </CODE><CODE> </CODE><TT>(</TT>border1<CODE> </CODE>b1_col;<CODE> </CODE>border2<CODE> </CODE>b1_col<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE> </CODE>s<CODE> </CODE>-&gt;<CODE> </CODE>failwith<CODE> </CODE><TT>(</TT><CODE>"display_border: unknown relief: "</CODE><CODE>^</CODE>s<TT>)</TT><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE>;;<BR><CODE>val display_border : border_state -&gt; component -&gt; component -&gt; unit -&gt; unit =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The function which creates a border takes a component and a list of options, it
constructs a <I>panel</I> containing that component.


<PRE><BR># <B>let</B><CODE> </CODE>create_border<CODE> </CODE>c<CODE> </CODE>lopt<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>bs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_border_state<CODE> </CODE>lopt<CODE> </CODE><B>in</B><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_panel<CODE> </CODE><B>true</B><CODE> </CODE><TT>(</TT>c<CODE>.</CODE>w<CODE> </CODE><CODE>+</CODE><CODE> </CODE><CODE>2</CODE><CODE> </CODE><CODE>*</CODE><CODE> </CODE>bs<CODE>.</CODE>size<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>c<CODE>.</CODE>h<CODE> </CODE><CODE>+</CODE><CODE> </CODE><CODE>2</CODE><CODE> </CODE><CODE>*</CODE><CODE> </CODE>bs<CODE>.</CODE>size<TT>)</TT><CODE> </CODE>lopt<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_layout<CODE> </CODE><TT>(</TT>center_layout<CODE> </CODE>p<TT>)</TT><CODE> </CODE>p;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>p<CODE>.</CODE>display<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>display_border<CODE> </CODE>bs<CODE> </CODE>c<CODE> </CODE>p;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>add_component<CODE> </CODE>p<CODE> </CODE>c<CODE> </CODE>[];<CODE> </CODE>p;;<BR><CODE>val create_border : component -&gt; (string * opt_val) list -&gt; component = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
Now we can test creating a component with a border on the 
<I>label</I> component and the text entry field <TT>tf1</TT> 
defined by in our previous tests. The result is show in figure <A HREF="book-ora124.html#fig-border">13.6</A>.


<PRE><BR># remove_component<CODE> </CODE><CODE> </CODE><CODE> </CODE>p1<CODE> </CODE>l1;;<BR># remove_component<CODE> </CODE><CODE> </CODE><CODE> </CODE>p4<CODE> </CODE>tf1;;<BR># <B>let</B><CODE> </CODE>b1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_border<CODE> </CODE>l1<CODE> </CODE>[];;<BR># <B>let</B><CODE> </CODE>b2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_border<CODE> </CODE>tf1<CODE> </CODE><CODE>[</CODE><CODE>"Relief"</CODE><CODE>,</CODE><CODE> </CODE>Sopt<CODE> </CODE><CODE>"Top"</CODE>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>Graphics.red;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Border_size"</CODE><CODE>,</CODE><CODE> </CODE>Iopt<CODE> </CODE><CODE>4</CODE><CODE>]</CODE>;;<BR># <B>let</B><CODE> </CODE>p5<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_panel<CODE> </CODE><B>true</B><CODE> </CODE><CODE>1</CODE><CODE>4</CODE><CODE>0</CODE><CODE> </CODE><CODE>8</CODE><CODE>0</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray2<CODE>]</CODE>;;<BR># set_layout<CODE> </CODE><CODE> </CODE><TT>(</TT>grid_layout<CODE> </CODE><TT>(</TT><CODE>1</CODE><CODE>,</CODE><CODE>2</CODE><TT>)</TT><CODE> </CODE>p5<TT>)</TT><CODE> </CODE>p5;;<BR># add_component<CODE> </CODE>p5<CODE> </CODE>b1<CODE> </CODE><CODE>[</CODE><CODE>"Row"</CODE><CODE>,</CODE><CODE> </CODE>Iopt<CODE> </CODE><CODE>1</CODE><CODE>]</CODE>;;<CODE> </CODE><CODE> </CODE><BR># add_component<CODE> </CODE>p5<CODE> </CODE>b2<CODE> </CODE><CODE>[</CODE><CODE>"Row"</CODE><CODE>,</CODE><CODE> </CODE>Iopt<CODE> </CODE><CODE>0</CODE><CODE>]</CODE>;;<CODE> </CODE><CODE> </CODE><BR>

</PRE>
<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora053.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 13.6: An enriched component.</DIV><BR>

<A NAME="fig-border"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE><A NAME="toc171"></A>
<H3> Setting up the <TT>Awi</TT> Library</H3><A NAME="sec-UPI"></A>The essential parts of our library have now been written. All declarations
<A NAME="text35" HREF="book-ora126.html#note35"><SUP><FONT SIZE=2>2</FONT></SUP></A> of types and values
which we have seen so far in this section can be grouped together in one
file. This library consists of one single module. If the file is called
<TT>awi.ml</TT> then we get a module called <TT>Awi</TT>. The link between
the name of the file and that of the module is described in chapter
<A HREF="index.html#chap-PM">14</A>.<BR>
<BR>
Compiling this file will produce a compiled interface file
<TT>awi.cmi</TT> and, depending on the compiler being used,
the bytecode itself <TT>awi.cmo</TT> or else the
native machine code <TT>awi.cmx</TT>. To use the bytecode compiler
we enter the following command
<PRE>
ocamlc -c awi.ml
</PRE>To use it at the interactive toplevel, we need to load the bytecode of
our new library with the command
<TT>#</TT><TT>load "awi.cmo";;</TT> having also previously ensured
that we have loaded the <TT>Graphics</TT> library. 
We can then start calling functions 
from the module to create and work with components.
<PRE>
# open Awi;;
# create_component;;
- : int -&gt; int -&gt; Awi.component = &lt;fun&gt;
</PRE>The result type of this function is 
<TT>Awi.component</TT>, chapter <A HREF="index.html#chap-PM">14</A> explains more about this.<BR>
<BR>
<A NAME="toc172"></A>
<H3> Example: A Franc-Euro Converter</H3>
We will now build a currency converter between Francs and Euros using 
this new library. The actual job of conversion is trivial, but the 
construction of the interface will show how the components communicate
with each other. While we are getting used to the new currency we need to
convert in both directions. Here are the components we have chosen:
<UL>
<LI>
 a list of two choices to describe the direction of the conversion;

<LI> two text entry fields for inputting values and displaying converted results;

<LI> a simple button to request that the calculation be performed;

<LI> two <I>labels</I> to show the meaning of each text entry field.
</UL>
These different components are shown in figure <A HREF="book-ora124.html#fig-conv">13.7</A>.<BR>
<BR>
Communication between the components is implemented by sharing state. For this
purpose we define the type <I>state_conv</I> which hold the fields for
francs (<TT>a</TT>), euros (<TT>b</TT>), the direction in which the
conversion is to be performed (<TT>dir</TT>) and the conversion factors
(<TT>fa</TT> and <TT>fb</TT>).


<PRE><BR># <B>type</B><CODE> </CODE>state_conv<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE><B>mutable</B><CODE> </CODE>a<CODE>:</CODE>float;<CODE> </CODE><B>mutable</B><CODE> </CODE>b<CODE>:</CODE>float;<CODE> </CODE><B>mutable</B><CODE> </CODE>dir<CODE> </CODE><CODE>:</CODE><CODE> </CODE>bool;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>fa<CODE> </CODE><CODE>:</CODE><CODE> </CODE>float;<CODE> </CODE>fb<CODE> </CODE><CODE>:</CODE><CODE> </CODE>float<CODE> </CODE>}<CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
We define the initial state as follows:


<PRE><BR># <B>let</B><CODE> </CODE>e<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE><CODE>6</CODE><CODE>.</CODE><CODE>5</CODE><CODE>5</CODE><CODE>9</CODE><CODE>5</CODE><CODE>7</CODE><CODE>0</CODE><CODE>7</CODE><CODE>4</CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>fe<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{<CODE> </CODE>a<CODE> </CODE><CODE>=</CODE><CODE>0</CODE><CODE>.</CODE><CODE>0</CODE>;<CODE> </CODE>b<CODE>=</CODE><CODE>0</CODE><CODE>.</CODE><CODE>0</CODE>;<CODE> </CODE>dir<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>true</B>;<CODE> </CODE>fa<CODE> </CODE><CODE>=</CODE><CODE> </CODE>e;<CODE> </CODE>fb<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>1</CODE><CODE>./.</CODE><CODE> </CODE>e};;<BR>

</PRE>
<BR>
<BR>
The conversion function returns a floating result following the direction
of the conversion.


<PRE><BR># <B>let</B><CODE> </CODE>calculate<CODE> </CODE>fe<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>fe<CODE>.</CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE>fe<CODE>.</CODE>b<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>fe<CODE>.</CODE>a<CODE> </CODE><CODE>/.</CODE><CODE> </CODE>fe<CODE>.</CODE>fa<CODE> </CODE><B>else</B><CODE> </CODE>fe<CODE>.</CODE>a<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>fe<CODE>.</CODE>b<CODE> </CODE><CODE>/.</CODE><CODE> </CODE>fe<CODE>.</CODE>fb;;<BR><CODE>val calculate : state_conv -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
A mouse click on the list of two choices changes the direction of the
conversion. The text of the choice strings is <CODE>"-&gt;"</CODE> and <CODE>"&lt;-"</CODE>.


<PRE><BR># <B>let</B><CODE> </CODE>action_dir<CODE> </CODE>fe<CODE> </CODE>cs<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>match</B><CODE> </CODE>get_cs_text<CODE> </CODE>cs<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"-&gt;"</CODE><CODE> </CODE>-&gt;<CODE> </CODE>fe<CODE>.</CODE>dir<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><B>true</B><BR><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>"&lt;-"</CODE><CODE> </CODE>-&gt;<CODE> </CODE>fe<CODE>.</CODE>dir<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE><B>false</B><BR><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE>failwith<CODE> </CODE><CODE>"action_dir"</CODE>;;<BR><CODE>val action_dir : state_conv -&gt; choice_state -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The action associated with the simple button causes the calculation
to be performed and displays the result in one of the two text entry
fields. For this to be possible we pass the two text entry fields as 
parameters to the action.


<PRE><BR># <B>let</B><CODE> </CODE>action_go<CODE> </CODE>fe<CODE> </CODE>tf_fr<CODE> </CODE>tf_eu<CODE> </CODE>tfs_fr<CODE> </CODE>tfs_eu<CODE> </CODE><CODE> </CODE>x<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>if</B><CODE> </CODE>fe<CODE>.</CODE>dir<CODE> </CODE><B>then</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>r<CODE> </CODE><CODE>=</CODE><CODE> </CODE>float_of_string<CODE> </CODE><TT>(</TT>get_tfs_text<CODE> </CODE>tfs_fr<TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>fe<CODE>.</CODE>a<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>r;<CODE> </CODE>calculate<CODE> </CODE>fe;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sr<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Printf.sprintf<CODE> </CODE><CODE>"%.2f"</CODE><CODE> </CODE>fe<CODE>.</CODE>b<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_tfs_text<CODE> </CODE>tf_eu<CODE> </CODE>tfs_eu<CODE> </CODE><CODE> </CODE>sr<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>else</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>r<CODE> </CODE><CODE>=</CODE><CODE> </CODE>float_of_string<CODE> </CODE><TT>(</TT>get_tfs_text<CODE> </CODE>tfs_eu<TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>fe<CODE>.</CODE>b<CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>r;<CODE> </CODE>calculate<CODE> </CODE>fe;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sr<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Printf.sprintf<CODE> </CODE><CODE>"%.2f"</CODE><CODE> </CODE>fe<CODE>.</CODE>a<CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_tfs_text<CODE> </CODE>tf_fr<CODE> </CODE>tfs_fr<CODE> </CODE>sr;;<BR><CODE>val action_go :</CODE><BR><CODE>  state_conv -&gt;</CODE><BR><CODE>  component -&gt; component -&gt; textfield_state -&gt; textfield_state -&gt; 'a -&gt; unit =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
It now remains to build the interface. The following function takes a width, a
height and a conversion state and returns the main container with the three
active components.<BR>
<BR>


<PRE><BR># <B>let</B><CODE> </CODE>create_conv<CODE> </CODE>w<CODE> </CODE>h<CODE> </CODE>fe<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>gray1<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>Graphics.rgb<CODE> </CODE><CODE>1</CODE><CODE>2</CODE><CODE>0</CODE><CODE> </CODE><CODE>1</CODE><CODE>2</CODE><CODE>0</CODE><CODE> </CODE><CODE>1</CODE><CODE>2</CODE><CODE>0</CODE><TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><B>let</B><CODE> </CODE>m<CODE> </CODE><CODE>=</CODE><CODE> </CODE>open_main_window<CODE> </CODE>w<CODE> </CODE>h<BR><CODE> </CODE><B>and</B><CODE> </CODE><CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_panel<CODE> </CODE><B>true</B><CODE> </CODE><TT>(</TT>w<CODE>-</CODE><CODE>4</CODE><TT>)</TT><CODE> </CODE><TT>(</TT>h<CODE>-</CODE><CODE>4</CODE><TT>)</TT><CODE> </CODE>[]<BR><CODE> </CODE><B>and</B><CODE> </CODE><CODE> </CODE>l1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_label<CODE> </CODE><CODE>"Francs"</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray1<CODE>]</CODE><BR><CODE> </CODE><B>and</B><CODE> </CODE>l2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_label<CODE> </CODE><CODE>"Euros"</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray1<CODE>]</CODE><BR><CODE> </CODE><B>and</B><CODE> </CODE>c<CODE>,</CODE>cs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_choice<CODE> </CODE><CODE>[</CODE><CODE>"-&gt;"</CODE>;<CODE> </CODE><CODE>"&lt;-"</CODE><CODE>]</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_18<CODE>]</CODE><BR><CODE> </CODE><B>and</B><CODE> </CODE>tf1<CODE>,</CODE>tfs1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_text_field<CODE> </CODE><CODE> </CODE><CODE>"0"</CODE><CODE> </CODE><CODE>1</CODE><CODE>0</CODE><CODE> </CODE><B>false</B><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_18<CODE>]</CODE><BR><CODE> </CODE><B>and</B><CODE> </CODE>tf2<CODE>,</CODE>tfs2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_text_field<CODE> </CODE><CODE>"0"</CODE><CODE> </CODE><CODE>1</CODE><CODE>0</CODE><CODE> </CODE><B>false</B><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_18<CODE>]</CODE><BR><CODE> </CODE><B>and</B><CODE> </CODE>b<CODE>,</CODE>bs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_button<CODE> </CODE><CODE>" Go "</CODE><CODE> </CODE><CODE>[</CODE><CODE>"Font"</CODE><CODE>,</CODE><CODE> </CODE>courier_bold_24<CODE>]</CODE><BR><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>gc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>get_gc<CODE> </CODE>m<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_gc_bcol<CODE> </CODE>gc<CODE> </CODE>gray1;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_layout<CODE> </CODE><TT>(</TT>grid_layout<CODE> </CODE><TT>(</TT><CODE>3</CODE><CODE>,</CODE><CODE>2</CODE><TT>)</TT><CODE> </CODE>m<CODE> </CODE><TT>)</TT><CODE> </CODE>m;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>tb1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_border<CODE> </CODE>tf1<CODE> </CODE><CODE> </CODE>[]<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>tb2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_border<CODE> </CODE>tf2<CODE> </CODE><CODE> </CODE>[]<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>bc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_border<CODE> </CODE>c<CODE> </CODE><CODE> </CODE>[]<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>bb<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>create_border<CODE> </CODE><CODE> </CODE>b<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>[</CODE><CODE>"Border_size"</CODE><CODE>,</CODE><CODE> </CODE>Iopt<CODE> </CODE><CODE>4</CODE>;<CODE> </CODE><CODE>"Relief"</CODE><CODE>,</CODE><CODE> </CODE>Sopt<CODE> </CODE><CODE>"Bot"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>"Background"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>gray2;<CODE> </CODE><CODE>"Background2"</CODE><CODE>,</CODE><CODE> </CODE>Copt<CODE> </CODE>Graphics.black<CODE>]</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_cs_action<CODE> </CODE>cs<CODE> </CODE><TT>(</TT>action_dir<CODE> </CODE>fe<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>set_bs_action<CODE> </CODE>bs<CODE> </CODE><TT>(</TT>action_go<CODE> </CODE>fe<CODE> </CODE>tf1<CODE> </CODE>tf2<CODE> </CODE>tfs1<CODE> </CODE>tfs2<TT>)</TT>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>add_component<CODE> </CODE>m<CODE> </CODE>l1<CODE> </CODE><CODE>[</CODE><CODE>"Col"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>0</CODE>;<CODE>"Row"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>1</CODE><CODE>]</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>add_component<CODE> </CODE>m<CODE> </CODE>l2<CODE> </CODE><CODE>[</CODE><CODE>"Col"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>2</CODE>;<CODE>"Row"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>1</CODE><CODE>]</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>add_component<CODE> </CODE>m<CODE> </CODE>bc<CODE> </CODE><CODE>[</CODE><CODE>"Col"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>1</CODE>;<CODE>"Row"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>1</CODE><CODE>]</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>add_component<CODE> </CODE>m<CODE> </CODE>tb1<CODE> </CODE><CODE>[</CODE><CODE>"Col"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>0</CODE>;<CODE>"Row"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>0</CODE><CODE>]</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>add_component<CODE> </CODE>m<CODE> </CODE>tb2<CODE> </CODE><CODE>[</CODE><CODE>"Col"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>2</CODE>;<CODE>"Row"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>0</CODE><CODE>]</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>add_component<CODE> </CODE>m<CODE> </CODE>bb<CODE> </CODE><CODE> </CODE><CODE>[</CODE><CODE>"Col"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>1</CODE>;<CODE>"Row"</CODE><CODE>,</CODE>Iopt<CODE> </CODE><CODE>0</CODE><CODE>]</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>m<CODE>,</CODE>bs<CODE>,</CODE>tf1<CODE>,</CODE>tf2;;<BR><CODE>val create_conv :</CODE><BR><CODE>  int -&gt;</CODE><BR><CODE>  int -&gt; state_conv -&gt; component * button_state * component * component =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The event handling loop is started on the container 
<TT>m</TT> constructed below. The resulting display is shown in figure
<A HREF="book-ora124.html#fig-conv">13.7</A>. 


<PRE><BR># <B>let</B><CODE> </CODE><TT>(</TT>m<CODE>,</CODE>c<CODE>,</CODE>t1<CODE>,</CODE>t2<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>create_conv<CODE> </CODE><CODE>4</CODE><CODE>2</CODE><CODE>0</CODE><CODE> </CODE><CODE>1</CODE><CODE>5</CODE><CODE>0</CODE><CODE> </CODE>fe<CODE> </CODE>;;<CODE> </CODE><BR># display<CODE> </CODE>m<CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora054.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 13.7: Calculator window.</DIV><BR>

<A NAME="fig-conv"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>
One click on the choice list changes both the displayed text and the
direction of the conversion because all the event handling closures share
the same state.<BR>
<BR>
<A NAME="toc173"></A>
<H3> Where to go from here</H3>
Closures allow us to register handling methods with graphical components.
It is however impossible to ``reopen'' these closures to 
extend an existing handler with additional behavior. We need to 
define a completely new handler. We discuss the possibilities for extending
handlers in chapter
<A HREF="index.html#chap-C-Organisation">16</A> where we compare the functional and object-oriented
paradigms.<BR>
<BR>
In our application many of the structures declared have fields with
identical names (for example <TT>txt</TT>). The last declaration masks
all previous occurences. This means that it becomes difficult to use the
field names directly and this is why we have declared a 
set of access functions for every type we have defined.
Another possibility would be
to cut our library up into several modules. From then on field names could
be disambiguated by using the module names. Nonetheless, with the help
of the access functions, we can already make full use of the library.
Chapter <A HREF="index.html#chap-PM">14</A> returns to the topic of type overlaying and introduces
abstract data types. The use of overlaying can, among other things, 
increase robustness by preventing the modification of sensitive data fields,
such as the parent child relationships between the components which should
not allow the construction of a circular graph.<BR>
<BR>
There are many possible ways to improve this library.<BR>
<BR>
One criterion in our design for components was that it should be possible
to write new ones. It is fairly easy to create components of an arbitrary
shape by using new definitions of the <TT>mem</TT> and <TT>display</TT>
functions. In this way one could create buttons which have an oval or
tear-shaped form.<BR>
<BR>
The few layout algorithms presented are not as helpful as they could be.
One could add a grid layout whose squares are of variable size and width.
Or maybe we want to place components alongside each other so long as there is
enough room. Finally we should anticipate the possibility that a 
change to the size of a component may be propagated to its children.<BR>
<BR>
<BR>
<BR>
<HR>
<A HREF="book-ora123.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora125.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
</BODY>
</HTML>
